roojs-ui.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     };
4725
4726     if(config && config.data){
4727         this.inlineData = config.data;
4728         delete config.data;
4729     }
4730
4731     Roo.apply(this, config);
4732     
4733     if(this.reader){ // reader passed
4734         this.reader = Roo.factory(this.reader, Roo.data);
4735         this.reader.xmodule = this.xmodule || false;
4736         if(!this.recordType){
4737             this.recordType = this.reader.recordType;
4738         }
4739         if(this.reader.onMetaChange){
4740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4741         }
4742     }
4743
4744     if(this.recordType){
4745         this.fields = this.recordType.prototype.fields;
4746     }
4747     this.modified = [];
4748
4749     this.addEvents({
4750         /**
4751          * @event datachanged
4752          * Fires when the data cache has changed, and a widget which is using this Store
4753          * as a Record cache should refresh its view.
4754          * @param {Store} this
4755          */
4756         datachanged : true,
4757         /**
4758          * @event metachange
4759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4760          * @param {Store} this
4761          * @param {Object} meta The JSON metadata
4762          */
4763         metachange : true,
4764         /**
4765          * @event add
4766          * Fires when Records have been added to the Store
4767          * @param {Store} this
4768          * @param {Roo.data.Record[]} records The array of Records added
4769          * @param {Number} index The index at which the record(s) were added
4770          */
4771         add : true,
4772         /**
4773          * @event remove
4774          * Fires when a Record has been removed from the Store
4775          * @param {Store} this
4776          * @param {Roo.data.Record} record The Record that was removed
4777          * @param {Number} index The index at which the record was removed
4778          */
4779         remove : true,
4780         /**
4781          * @event update
4782          * Fires when a Record has been updated
4783          * @param {Store} this
4784          * @param {Roo.data.Record} record The Record that was updated
4785          * @param {String} operation The update operation being performed.  Value may be one of:
4786          * <pre><code>
4787  Roo.data.Record.EDIT
4788  Roo.data.Record.REJECT
4789  Roo.data.Record.COMMIT
4790          * </code></pre>
4791          */
4792         update : true,
4793         /**
4794          * @event clear
4795          * Fires when the data cache has been cleared.
4796          * @param {Store} this
4797          */
4798         clear : true,
4799         /**
4800          * @event beforeload
4801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4802          * the load action will be canceled.
4803          * @param {Store} this
4804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4805          */
4806         beforeload : true,
4807         /**
4808          * @event load
4809          * Fires after a new set of Records has been loaded.
4810          * @param {Store} this
4811          * @param {Roo.data.Record[]} records The Records that were loaded
4812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4813          */
4814         load : true,
4815         /**
4816          * @event loadexception
4817          * Fires if an exception occurs in the Proxy during loading.
4818          * Called with the signature of the Proxy's "loadexception" event.
4819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4820          * 
4821          * @param {Proxy} 
4822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4823          * @param {Object} load options 
4824          * @param {Object} jsonData from your request (normally this contains the Exception)
4825          */
4826         loadexception : true
4827     });
4828     
4829     if(this.proxy){
4830         this.proxy = Roo.factory(this.proxy, Roo.data);
4831         this.proxy.xmodule = this.xmodule || false;
4832         this.relayEvents(this.proxy,  ["loadexception"]);
4833     }
4834     this.sortToggle = {};
4835
4836     Roo.data.Store.superclass.constructor.call(this);
4837
4838     if(this.inlineData){
4839         this.loadData(this.inlineData);
4840         delete this.inlineData;
4841     }
4842 };
4843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4844      /**
4845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4846     * without a remote query - used by combo/forms at present.
4847     */
4848     
4849     /**
4850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4851     */
4852     /**
4853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4854     */
4855     /**
4856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4858     */
4859     /**
4860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4861     * on any HTTP request
4862     */
4863     /**
4864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4865     */
4866     /**
4867     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4868     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4869     */
4870     remoteSort : false,
4871
4872     /**
4873     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4874      * loaded or when a record is removed. (defaults to false).
4875     */
4876     pruneModifiedRecords : false,
4877
4878     // private
4879     lastOptions : null,
4880
4881     /**
4882      * Add Records to the Store and fires the add event.
4883      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4884      */
4885     add : function(records){
4886         records = [].concat(records);
4887         for(var i = 0, len = records.length; i < len; i++){
4888             records[i].join(this);
4889         }
4890         var index = this.data.length;
4891         this.data.addAll(records);
4892         this.fireEvent("add", this, records, index);
4893     },
4894
4895     /**
4896      * Remove a Record from the Store and fires the remove event.
4897      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4898      */
4899     remove : function(record){
4900         var index = this.data.indexOf(record);
4901         this.data.removeAt(index);
4902         if(this.pruneModifiedRecords){
4903             this.modified.remove(record);
4904         }
4905         this.fireEvent("remove", this, record, index);
4906     },
4907
4908     /**
4909      * Remove all Records from the Store and fires the clear event.
4910      */
4911     removeAll : function(){
4912         this.data.clear();
4913         if(this.pruneModifiedRecords){
4914             this.modified = [];
4915         }
4916         this.fireEvent("clear", this);
4917     },
4918
4919     /**
4920      * Inserts Records to the Store at the given index and fires the add event.
4921      * @param {Number} index The start index at which to insert the passed Records.
4922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4923      */
4924     insert : function(index, records){
4925         records = [].concat(records);
4926         for(var i = 0, len = records.length; i < len; i++){
4927             this.data.insert(index, records[i]);
4928             records[i].join(this);
4929         }
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the passed Record.
4935      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4936      * @return {Number} The index of the passed Record. Returns -1 if not found.
4937      */
4938     indexOf : function(record){
4939         return this.data.indexOf(record);
4940     },
4941
4942     /**
4943      * Get the index within the cache of the Record with the passed id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Number} The index of the Record. Returns -1 if not found.
4946      */
4947     indexOfId : function(id){
4948         return this.data.indexOfKey(id);
4949     },
4950
4951     /**
4952      * Get the Record with the specified id.
4953      * @param {String} id The id of the Record to find.
4954      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4955      */
4956     getById : function(id){
4957         return this.data.key(id);
4958     },
4959
4960     /**
4961      * Get the Record at the specified index.
4962      * @param {Number} index The index of the Record to find.
4963      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4964      */
4965     getAt : function(index){
4966         return this.data.itemAt(index);
4967     },
4968
4969     /**
4970      * Returns a range of Records between specified indices.
4971      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4972      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4973      * @return {Roo.data.Record[]} An array of Records
4974      */
4975     getRange : function(start, end){
4976         return this.data.getRange(start, end);
4977     },
4978
4979     // private
4980     storeOptions : function(o){
4981         o = Roo.apply({}, o);
4982         delete o.callback;
4983         delete o.scope;
4984         this.lastOptions = o;
4985     },
4986
4987     /**
4988      * Loads the Record cache from the configured Proxy using the configured Reader.
4989      * <p>
4990      * If using remote paging, then the first load call must specify the <em>start</em>
4991      * and <em>limit</em> properties in the options.params property to establish the initial
4992      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4993      * <p>
4994      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4995      * and this call will return before the new data has been loaded. Perform any post-processing
4996      * in a callback function, or in a "load" event handler.</strong>
4997      * <p>
4998      * @param {Object} options An object containing properties which control loading options:<ul>
4999      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5000      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5001      * passed the following arguments:<ul>
5002      * <li>r : Roo.data.Record[]</li>
5003      * <li>options: Options object from the load call</li>
5004      * <li>success: Boolean success indicator</li></ul></li>
5005      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5006      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5007      * </ul>
5008      */
5009     load : function(options){
5010         options = options || {};
5011         if(this.fireEvent("beforeload", this, options) !== false){
5012             this.storeOptions(options);
5013             var p = Roo.apply(options.params || {}, this.baseParams);
5014             // if meta was not loaded from remote source.. try requesting it.
5015             if (!this.reader.metaFromRemote) {
5016                 p._requestMeta = 1;
5017             }
5018             if(this.sortInfo && this.remoteSort){
5019                 var pn = this.paramNames;
5020                 p[pn["sort"]] = this.sortInfo.field;
5021                 p[pn["dir"]] = this.sortInfo.direction;
5022             }
5023             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5024         }
5025     },
5026
5027     /**
5028      * Reloads the Record cache from the configured Proxy using the configured Reader and
5029      * the options from the last load operation performed.
5030      * @param {Object} options (optional) An object containing properties which may override the options
5031      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5032      * the most recently used options are reused).
5033      */
5034     reload : function(options){
5035         this.load(Roo.applyIf(options||{}, this.lastOptions));
5036     },
5037
5038     // private
5039     // Called as a callback by the Reader during a load operation.
5040     loadRecords : function(o, options, success){
5041         if(!o || success === false){
5042             if(success !== false){
5043                 this.fireEvent("load", this, [], options);
5044             }
5045             if(options.callback){
5046                 options.callback.call(options.scope || this, [], options, false);
5047             }
5048             return;
5049         }
5050         // if data returned failure - throw an exception.
5051         if (o.success === false) {
5052             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5053             return;
5054         }
5055         var r = o.records, t = o.totalRecords || r.length;
5056         if(!options || options.add !== true){
5057             if(this.pruneModifiedRecords){
5058                 this.modified = [];
5059             }
5060             for(var i = 0, len = r.length; i < len; i++){
5061                 r[i].join(this);
5062             }
5063             if(this.snapshot){
5064                 this.data = this.snapshot;
5065                 delete this.snapshot;
5066             }
5067             this.data.clear();
5068             this.data.addAll(r);
5069             this.totalLength = t;
5070             this.applySort();
5071             this.fireEvent("datachanged", this);
5072         }else{
5073             this.totalLength = Math.max(t, this.data.length+r.length);
5074             this.add(r);
5075         }
5076         this.fireEvent("load", this, r, options);
5077         if(options.callback){
5078             options.callback.call(options.scope || this, r, options, true);
5079         }
5080     },
5081
5082     /**
5083      * Loads data from a passed data block. A Reader which understands the format of the data
5084      * must have been configured in the constructor.
5085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5088      */
5089     loadData : function(o, append){
5090         var r = this.reader.readRecords(o);
5091         this.loadRecords(r, {add: append}, true);
5092     },
5093
5094     /**
5095      * Gets the number of cached records.
5096      * <p>
5097      * <em>If using paging, this may not be the total size of the dataset. If the data object
5098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5099      * the data set size</em>
5100      */
5101     getCount : function(){
5102         return this.data.length || 0;
5103     },
5104
5105     /**
5106      * Gets the total number of records in the dataset as returned by the server.
5107      * <p>
5108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5109      * the dataset size</em>
5110      */
5111     getTotalCount : function(){
5112         return this.totalLength || 0;
5113     },
5114
5115     /**
5116      * Returns the sort state of the Store as an object with two properties:
5117      * <pre><code>
5118  field {String} The name of the field by which the Records are sorted
5119  direction {String} The sort order, "ASC" or "DESC"
5120      * </code></pre>
5121      */
5122     getSortState : function(){
5123         return this.sortInfo;
5124     },
5125
5126     // private
5127     applySort : function(){
5128         if(this.sortInfo && !this.remoteSort){
5129             var s = this.sortInfo, f = s.field;
5130             var st = this.fields.get(f).sortType;
5131             var fn = function(r1, r2){
5132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5134             };
5135             this.data.sort(s.direction, fn);
5136             if(this.snapshot && this.snapshot != this.data){
5137                 this.snapshot.sort(s.direction, fn);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * Sets the default sort column and order to be used by the next load operation.
5144      * @param {String} fieldName The name of the field to sort by.
5145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5146      */
5147     setDefaultSort : function(field, dir){
5148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5149     },
5150
5151     /**
5152      * Sort the Records.
5153      * If remote sorting is used, the sort is performed on the server, and the cache is
5154      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
5159         var f = this.fields.get(fieldName);
5160         if(!dir){
5161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5162                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5163             }else{
5164                 dir = f.sortDir;
5165             }
5166         }
5167         this.sortToggle[f.name] = dir;
5168         this.sortInfo = {field: f.name, direction: dir};
5169         if(!this.remoteSort){
5170             this.applySort();
5171             this.fireEvent("datachanged", this);
5172         }else{
5173             this.load(this.lastOptions);
5174         }
5175     },
5176
5177     /**
5178      * Calls the specified function for each of the Records in the cache.
5179      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5180      * Returning <em>false</em> aborts and exits the iteration.
5181      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5182      */
5183     each : function(fn, scope){
5184         this.data.each(fn, scope);
5185     },
5186
5187     /**
5188      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5189      * (e.g., during paging).
5190      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5191      */
5192     getModifiedRecords : function(){
5193         return this.modified;
5194     },
5195
5196     // private
5197     createFilterFn : function(property, value, anyMatch){
5198         if(!value.exec){ // not a regex
5199             value = String(value);
5200             if(value.length == 0){
5201                 return false;
5202             }
5203             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5204         }
5205         return function(r){
5206             return value.test(r.data[property]);
5207         };
5208     },
5209
5210     /**
5211      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5212      * @param {String} property A field on your records
5213      * @param {Number} start The record index to start at (defaults to 0)
5214      * @param {Number} end The last record index to include (defaults to length - 1)
5215      * @return {Number} The sum
5216      */
5217     sum : function(property, start, end){
5218         var rs = this.data.items, v = 0;
5219         start = start || 0;
5220         end = (end || end === 0) ? end : rs.length-1;
5221
5222         for(var i = start; i <= end; i++){
5223             v += (rs[i].data[property] || 0);
5224         }
5225         return v;
5226     },
5227
5228     /**
5229      * Filter the records by a specified property.
5230      * @param {String} field A field on your records
5231      * @param {String/RegExp} value Either a string that the field
5232      * should start with or a RegExp to test against the field
5233      * @param {Boolean} anyMatch True to match any part not just the beginning
5234      */
5235     filter : function(property, value, anyMatch){
5236         var fn = this.createFilterFn(property, value, anyMatch);
5237         return fn ? this.filterBy(fn) : this.clearFilter();
5238     },
5239
5240     /**
5241      * Filter by a function. The specified function will be called with each
5242      * record in this data source. If the function returns true the record is included,
5243      * otherwise it is filtered.
5244      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5245      * @param {Object} scope (optional) The scope of the function (defaults to this)
5246      */
5247     filterBy : function(fn, scope){
5248         this.snapshot = this.snapshot || this.data;
5249         this.data = this.queryBy(fn, scope||this);
5250         this.fireEvent("datachanged", this);
5251     },
5252
5253     /**
5254      * Query the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5260      */
5261     query : function(property, value, anyMatch){
5262         var fn = this.createFilterFn(property, value, anyMatch);
5263         return fn ? this.queryBy(fn) : this.data.clone();
5264     },
5265
5266     /**
5267      * Query by a function. The specified function will be called with each
5268      * record in this data source. If the function returns true the record is included
5269      * in the results.
5270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5271      * @param {Object} scope (optional) The scope of the function (defaults to this)
5272       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      **/
5274     queryBy : function(fn, scope){
5275         var data = this.snapshot || this.data;
5276         return data.filterBy(fn, scope||this);
5277     },
5278
5279     /**
5280      * Collects unique values for a particular dataIndex from this store.
5281      * @param {String} dataIndex The property to collect
5282      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5283      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5284      * @return {Array} An array of the unique values
5285      **/
5286     collect : function(dataIndex, allowNull, bypassFilter){
5287         var d = (bypassFilter === true && this.snapshot) ?
5288                 this.snapshot.items : this.data.items;
5289         var v, sv, r = [], l = {};
5290         for(var i = 0, len = d.length; i < len; i++){
5291             v = d[i].data[dataIndex];
5292             sv = String(v);
5293             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5294                 l[sv] = true;
5295                 r[r.length] = v;
5296             }
5297         }
5298         return r;
5299     },
5300
5301     /**
5302      * Revert to a view of the Record cache with no filtering applied.
5303      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5304      */
5305     clearFilter : function(suppressEvent){
5306         if(this.snapshot && this.snapshot != this.data){
5307             this.data = this.snapshot;
5308             delete this.snapshot;
5309             if(suppressEvent !== true){
5310                 this.fireEvent("datachanged", this);
5311             }
5312         }
5313     },
5314
5315     // private
5316     afterEdit : function(record){
5317         if(this.modified.indexOf(record) == -1){
5318             this.modified.push(record);
5319         }
5320         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5321     },
5322
5323     // private
5324     afterReject : function(record){
5325         this.modified.remove(record);
5326         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5327     },
5328
5329     // private
5330     afterCommit : function(record){
5331         this.modified.remove(record);
5332         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5333     },
5334
5335     /**
5336      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5337      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5338      */
5339     commitChanges : function(){
5340         var m = this.modified.slice(0);
5341         this.modified = [];
5342         for(var i = 0, len = m.length; i < len; i++){
5343             m[i].commit();
5344         }
5345     },
5346
5347     /**
5348      * Cancel outstanding changes on all changed records.
5349      */
5350     rejectChanges : function(){
5351         var m = this.modified.slice(0);
5352         this.modified = [];
5353         for(var i = 0, len = m.length; i < len; i++){
5354             m[i].reject();
5355         }
5356     },
5357
5358     onMetaChange : function(meta, rtype, o){
5359         this.recordType = rtype;
5360         this.fields = rtype.prototype.fields;
5361         delete this.snapshot;
5362         this.sortInfo = meta.sortInfo || this.sortInfo;
5363         this.modified = [];
5364         this.fireEvent('metachange', this, this.reader.meta);
5365     }
5366 });/*
5367  * Based on:
5368  * Ext JS Library 1.1.1
5369  * Copyright(c) 2006-2007, Ext JS, LLC.
5370  *
5371  * Originally Released Under LGPL - original licence link has changed is not relivant.
5372  *
5373  * Fork - LGPL
5374  * <script type="text/javascript">
5375  */
5376
5377 /**
5378  * @class Roo.data.SimpleStore
5379  * @extends Roo.data.Store
5380  * Small helper class to make creating Stores from Array data easier.
5381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5382  * @cfg {Array} fields An array of field definition objects, or field name strings.
5383  * @cfg {Array} data The multi-dimensional array of data
5384  * @constructor
5385  * @param {Object} config
5386  */
5387 Roo.data.SimpleStore = function(config){
5388     Roo.data.SimpleStore.superclass.constructor.call(this, {
5389         isLocal : true,
5390         reader: new Roo.data.ArrayReader({
5391                 id: config.id
5392             },
5393             Roo.data.Record.create(config.fields)
5394         ),
5395         proxy : new Roo.data.MemoryProxy(config.data)
5396     });
5397     this.load();
5398 };
5399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411 /**
5412  * @extends Roo.data.Store
5413  * @class Roo.data.JsonStore
5414  * Small helper class to make creating Stores for JSON data easier. <br/>
5415 <pre><code>
5416 var store = new Roo.data.JsonStore({
5417     url: 'get-images.php',
5418     root: 'images',
5419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5420 });
5421 </code></pre>
5422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5423  * JsonReader and HttpProxy (unless inline data is provided).</b>
5424  * @cfg {Array} fields An array of field definition objects, or field name strings.
5425  * @constructor
5426  * @param {Object} config
5427  */
5428 Roo.data.JsonStore = function(c){
5429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5431         reader: new Roo.data.JsonReader(c, c.fields)
5432     }));
5433 };
5434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5435  * Based on:
5436  * Ext JS Library 1.1.1
5437  * Copyright(c) 2006-2007, Ext JS, LLC.
5438  *
5439  * Originally Released Under LGPL - original licence link has changed is not relivant.
5440  *
5441  * Fork - LGPL
5442  * <script type="text/javascript">
5443  */
5444
5445  
5446 Roo.data.Field = function(config){
5447     if(typeof config == "string"){
5448         config = {name: config};
5449     }
5450     Roo.apply(this, config);
5451     
5452     if(!this.type){
5453         this.type = "auto";
5454     }
5455     
5456     var st = Roo.data.SortTypes;
5457     // named sortTypes are supported, here we look them up
5458     if(typeof this.sortType == "string"){
5459         this.sortType = st[this.sortType];
5460     }
5461     
5462     // set default sortType for strings and dates
5463     if(!this.sortType){
5464         switch(this.type){
5465             case "string":
5466                 this.sortType = st.asUCString;
5467                 break;
5468             case "date":
5469                 this.sortType = st.asDate;
5470                 break;
5471             default:
5472                 this.sortType = st.none;
5473         }
5474     }
5475
5476     // define once
5477     var stripRe = /[\$,%]/g;
5478
5479     // prebuilt conversion function for this field, instead of
5480     // switching every time we're reading a value
5481     if(!this.convert){
5482         var cv, dateFormat = this.dateFormat;
5483         switch(this.type){
5484             case "":
5485             case "auto":
5486             case undefined:
5487                 cv = function(v){ return v; };
5488                 break;
5489             case "string":
5490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5491                 break;
5492             case "int":
5493                 cv = function(v){
5494                     return v !== undefined && v !== null && v !== '' ?
5495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5496                     };
5497                 break;
5498             case "float":
5499                 cv = function(v){
5500                     return v !== undefined && v !== null && v !== '' ?
5501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5502                     };
5503                 break;
5504             case "bool":
5505             case "boolean":
5506                 cv = function(v){ return v === true || v === "true" || v == 1; };
5507                 break;
5508             case "date":
5509                 cv = function(v){
5510                     if(!v){
5511                         return '';
5512                     }
5513                     if(v instanceof Date){
5514                         return v;
5515                     }
5516                     if(dateFormat){
5517                         if(dateFormat == "timestamp"){
5518                             return new Date(v*1000);
5519                         }
5520                         return Date.parseDate(v, dateFormat);
5521                     }
5522                     var parsed = Date.parse(v);
5523                     return parsed ? new Date(parsed) : null;
5524                 };
5525              break;
5526             
5527         }
5528         this.convert = cv;
5529     }
5530 };
5531
5532 Roo.data.Field.prototype = {
5533     dateFormat: null,
5534     defaultValue: "",
5535     mapping: null,
5536     sortType : null,
5537     sortDir : "ASC"
5538 };/*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 // Base class for reading structured data from a data source.  This class is intended to be
5550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5551
5552 /**
5553  * @class Roo.data.DataReader
5554  * Base class for reading structured data from a data source.  This class is intended to be
5555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5556  */
5557
5558 Roo.data.DataReader = function(meta, recordType){
5559     
5560     this.meta = meta;
5561     
5562     this.recordType = recordType instanceof Array ? 
5563         Roo.data.Record.create(recordType) : recordType;
5564 };
5565
5566 Roo.data.DataReader.prototype = {
5567      /**
5568      * Create an empty record
5569      * @param {Object} data (optional) - overlay some values
5570      * @return {Roo.data.Record} record created.
5571      */
5572     newRow :  function(d) {
5573         var da =  {};
5574         this.recordType.prototype.fields.each(function(c) {
5575             switch( c.type) {
5576                 case 'int' : da[c.name] = 0; break;
5577                 case 'date' : da[c.name] = new Date(); break;
5578                 case 'float' : da[c.name] = 0.0; break;
5579                 case 'boolean' : da[c.name] = false; break;
5580                 default : da[c.name] = ""; break;
5581             }
5582             
5583         });
5584         return new this.recordType(Roo.apply(da, d));
5585     }
5586     
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597
5598 /**
5599  * @class Roo.data.DataProxy
5600  * @extends Roo.data.Observable
5601  * This class is an abstract base class for implementations which provide retrieval of
5602  * unformatted data objects.<br>
5603  * <p>
5604  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5605  * (of the appropriate type which knows how to parse the data object) to provide a block of
5606  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5607  * <p>
5608  * Custom implementations must implement the load method as described in
5609  * {@link Roo.data.HttpProxy#load}.
5610  */
5611 Roo.data.DataProxy = function(){
5612     this.addEvents({
5613         /**
5614          * @event beforeload
5615          * Fires before a network request is made to retrieve a data object.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} params The params parameter to the load function.
5618          */
5619         beforeload : true,
5620         /**
5621          * @event load
5622          * Fires before the load method's callback is called.
5623          * @param {Object} This DataProxy object.
5624          * @param {Object} o The data object.
5625          * @param {Object} arg The callback argument object passed to the load function.
5626          */
5627         load : true,
5628         /**
5629          * @event loadexception
5630          * Fires if an Exception occurs during data retrieval.
5631          * @param {Object} This DataProxy object.
5632          * @param {Object} o The data object.
5633          * @param {Object} arg The callback argument object passed to the load function.
5634          * @param {Object} e The Exception.
5635          */
5636         loadexception : true
5637     });
5638     Roo.data.DataProxy.superclass.constructor.call(this);
5639 };
5640
5641 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5642
5643     /**
5644      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5645      */
5646 /*
5647  * Based on:
5648  * Ext JS Library 1.1.1
5649  * Copyright(c) 2006-2007, Ext JS, LLC.
5650  *
5651  * Originally Released Under LGPL - original licence link has changed is not relivant.
5652  *
5653  * Fork - LGPL
5654  * <script type="text/javascript">
5655  */
5656 /**
5657  * @class Roo.data.MemoryProxy
5658  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5659  * to the Reader when its load method is called.
5660  * @constructor
5661  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5662  */
5663 Roo.data.MemoryProxy = function(data){
5664     if (data.data) {
5665         data = data.data;
5666     }
5667     Roo.data.MemoryProxy.superclass.constructor.call(this);
5668     this.data = data;
5669 };
5670
5671 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5672     /**
5673      * Load data from the requested source (in this case an in-memory
5674      * data object passed to the constructor), read the data object into
5675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5676      * process that block using the passed callback.
5677      * @param {Object} params This parameter is not used by the MemoryProxy class.
5678      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5679      * object into a block of Roo.data.Records.
5680      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5681      * The function must be passed <ul>
5682      * <li>The Record block object</li>
5683      * <li>The "arg" argument from the load function</li>
5684      * <li>A boolean success indicator</li>
5685      * </ul>
5686      * @param {Object} scope The scope in which to call the callback
5687      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5688      */
5689     load : function(params, reader, callback, scope, arg){
5690         params = params || {};
5691         var result;
5692         try {
5693             result = reader.readRecords(this.data);
5694         }catch(e){
5695             this.fireEvent("loadexception", this, arg, null, e);
5696             callback.call(scope, null, arg, false);
5697             return;
5698         }
5699         callback.call(scope, result, arg, true);
5700     },
5701     
5702     // private
5703     update : function(params, records){
5704         
5705     }
5706 });/*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716 /**
5717  * @class Roo.data.HttpProxy
5718  * @extends Roo.data.DataProxy
5719  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5720  * configured to reference a certain URL.<br><br>
5721  * <p>
5722  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5723  * from which the running page was served.<br><br>
5724  * <p>
5725  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5726  * <p>
5727  * Be aware that to enable the browser to parse an XML document, the server must set
5728  * the Content-Type header in the HTTP response to "text/xml".
5729  * @constructor
5730  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5731  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5732  * will be used to make the request.
5733  */
5734 Roo.data.HttpProxy = function(conn){
5735     Roo.data.HttpProxy.superclass.constructor.call(this);
5736     // is conn a conn config or a real conn?
5737     this.conn = conn;
5738     this.useAjax = !conn || !conn.events;
5739   
5740 };
5741
5742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5743     // thse are take from connection...
5744     
5745     /**
5746      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5750      * extra parameters to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5754      *  to each request made by this object. (defaults to undefined)
5755      */
5756     /**
5757      * @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)
5758      */
5759     /**
5760      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5761      */
5762      /**
5763      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5764      * @type Boolean
5765      */
5766   
5767
5768     /**
5769      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5770      * @type Boolean
5771      */
5772     /**
5773      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5774      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5775      * a finer-grained basis than the DataProxy events.
5776      */
5777     getConnection : function(){
5778         return this.useAjax ? Roo.Ajax : this.conn;
5779     },
5780
5781     /**
5782      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5783      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5784      * process that block using the passed callback.
5785      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5786      * for the request to the remote server.
5787      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5788      * object into a block of Roo.data.Records.
5789      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5790      * The function must be passed <ul>
5791      * <li>The Record block object</li>
5792      * <li>The "arg" argument from the load function</li>
5793      * <li>A boolean success indicator</li>
5794      * </ul>
5795      * @param {Object} scope The scope in which to call the callback
5796      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5797      */
5798     load : function(params, reader, callback, scope, arg){
5799         if(this.fireEvent("beforeload", this, params) !== false){
5800             var  o = {
5801                 params : params || {},
5802                 request: {
5803                     callback : callback,
5804                     scope : scope,
5805                     arg : arg
5806                 },
5807                 reader: reader,
5808                 callback : this.loadResponse,
5809                 scope: this
5810             };
5811             if(this.useAjax){
5812                 Roo.applyIf(o, this.conn);
5813                 if(this.activeRequest){
5814                     Roo.Ajax.abort(this.activeRequest);
5815                 }
5816                 this.activeRequest = Roo.Ajax.request(o);
5817             }else{
5818                 this.conn.request(o);
5819             }
5820         }else{
5821             callback.call(scope||this, null, arg, false);
5822         }
5823     },
5824
5825     // private
5826     loadResponse : function(o, success, response){
5827         delete this.activeRequest;
5828         if(!success){
5829             this.fireEvent("loadexception", this, o, response);
5830             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5831             return;
5832         }
5833         var result;
5834         try {
5835             result = o.reader.read(response);
5836         }catch(e){
5837             this.fireEvent("loadexception", this, o, response, e);
5838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5839             return;
5840         }
5841         
5842         this.fireEvent("load", this, o, o.request.arg);
5843         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5844     },
5845
5846     // private
5847     update : function(dataSet){
5848
5849     },
5850
5851     // private
5852     updateResponse : function(dataSet){
5853
5854     }
5855 });/*
5856  * Based on:
5857  * Ext JS Library 1.1.1
5858  * Copyright(c) 2006-2007, Ext JS, LLC.
5859  *
5860  * Originally Released Under LGPL - original licence link has changed is not relivant.
5861  *
5862  * Fork - LGPL
5863  * <script type="text/javascript">
5864  */
5865
5866 /**
5867  * @class Roo.data.ScriptTagProxy
5868  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5869  * other than the originating domain of the running page.<br><br>
5870  * <p>
5871  * <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
5872  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5873  * <p>
5874  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5875  * source code that is used as the source inside a &lt;script> tag.<br><br>
5876  * <p>
5877  * In order for the browser to process the returned data, the server must wrap the data object
5878  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5879  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5880  * depending on whether the callback name was passed:
5881  * <p>
5882  * <pre><code>
5883 boolean scriptTag = false;
5884 String cb = request.getParameter("callback");
5885 if (cb != null) {
5886     scriptTag = true;
5887     response.setContentType("text/javascript");
5888 } else {
5889     response.setContentType("application/x-json");
5890 }
5891 Writer out = response.getWriter();
5892 if (scriptTag) {
5893     out.write(cb + "(");
5894 }
5895 out.print(dataBlock.toJsonString());
5896 if (scriptTag) {
5897     out.write(");");
5898 }
5899 </pre></code>
5900  *
5901  * @constructor
5902  * @param {Object} config A configuration object.
5903  */
5904 Roo.data.ScriptTagProxy = function(config){
5905     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5906     Roo.apply(this, config);
5907     this.head = document.getElementsByTagName("head")[0];
5908 };
5909
5910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5911
5912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5913     /**
5914      * @cfg {String} url The URL from which to request the data object.
5915      */
5916     /**
5917      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5918      */
5919     timeout : 30000,
5920     /**
5921      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5922      * the server the name of the callback function set up by the load call to process the returned data object.
5923      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5924      * javascript output which calls this named function passing the data object as its only parameter.
5925      */
5926     callbackParam : "callback",
5927     /**
5928      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5929      * name to the request.
5930      */
5931     nocache : true,
5932
5933     /**
5934      * Load data from the configured URL, read the data object into
5935      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5936      * process that block using the passed callback.
5937      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5938      * for the request to the remote server.
5939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5940      * object into a block of Roo.data.Records.
5941      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5942      * The function must be passed <ul>
5943      * <li>The Record block object</li>
5944      * <li>The "arg" argument from the load function</li>
5945      * <li>A boolean success indicator</li>
5946      * </ul>
5947      * @param {Object} scope The scope in which to call the callback
5948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5949      */
5950     load : function(params, reader, callback, scope, arg){
5951         if(this.fireEvent("beforeload", this, params) !== false){
5952
5953             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5954
5955             var url = this.url;
5956             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5957             if(this.nocache){
5958                 url += "&_dc=" + (new Date().getTime());
5959             }
5960             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5961             var trans = {
5962                 id : transId,
5963                 cb : "stcCallback"+transId,
5964                 scriptId : "stcScript"+transId,
5965                 params : params,
5966                 arg : arg,
5967                 url : url,
5968                 callback : callback,
5969                 scope : scope,
5970                 reader : reader
5971             };
5972             var conn = this;
5973
5974             window[trans.cb] = function(o){
5975                 conn.handleResponse(o, trans);
5976             };
5977
5978             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5979
5980             if(this.autoAbort !== false){
5981                 this.abort();
5982             }
5983
5984             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5985
5986             var script = document.createElement("script");
5987             script.setAttribute("src", url);
5988             script.setAttribute("type", "text/javascript");
5989             script.setAttribute("id", trans.scriptId);
5990             this.head.appendChild(script);
5991
5992             this.trans = trans;
5993         }else{
5994             callback.call(scope||this, null, arg, false);
5995         }
5996     },
5997
5998     // private
5999     isLoading : function(){
6000         return this.trans ? true : false;
6001     },
6002
6003     /**
6004      * Abort the current server request.
6005      */
6006     abort : function(){
6007         if(this.isLoading()){
6008             this.destroyTrans(this.trans);
6009         }
6010     },
6011
6012     // private
6013     destroyTrans : function(trans, isLoaded){
6014         this.head.removeChild(document.getElementById(trans.scriptId));
6015         clearTimeout(trans.timeoutId);
6016         if(isLoaded){
6017             window[trans.cb] = undefined;
6018             try{
6019                 delete window[trans.cb];
6020             }catch(e){}
6021         }else{
6022             // if hasn't been loaded, wait for load to remove it to prevent script error
6023             window[trans.cb] = function(){
6024                 window[trans.cb] = undefined;
6025                 try{
6026                     delete window[trans.cb];
6027                 }catch(e){}
6028             };
6029         }
6030     },
6031
6032     // private
6033     handleResponse : function(o, trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, true);
6036         var result;
6037         try {
6038             result = trans.reader.readRecords(o);
6039         }catch(e){
6040             this.fireEvent("loadexception", this, o, trans.arg, e);
6041             trans.callback.call(trans.scope||window, null, trans.arg, false);
6042             return;
6043         }
6044         this.fireEvent("load", this, o, trans.arg);
6045         trans.callback.call(trans.scope||window, result, trans.arg, true);
6046     },
6047
6048     // private
6049     handleFailure : function(trans){
6050         this.trans = false;
6051         this.destroyTrans(trans, false);
6052         this.fireEvent("loadexception", this, null, trans.arg);
6053         trans.callback.call(trans.scope||window, null, trans.arg, false);
6054     }
6055 });/*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 /**
6067  * @class Roo.data.JsonReader
6068  * @extends Roo.data.DataReader
6069  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6070  * based on mappings in a provided Roo.data.Record constructor.
6071  * 
6072  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6073  * in the reply previously. 
6074  * 
6075  * <p>
6076  * Example code:
6077  * <pre><code>
6078 var RecordDef = Roo.data.Record.create([
6079     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6080     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6081 ]);
6082 var myReader = new Roo.data.JsonReader({
6083     totalProperty: "results",    // The property which contains the total dataset size (optional)
6084     root: "rows",                // The property which contains an Array of row objects
6085     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6086 }, RecordDef);
6087 </code></pre>
6088  * <p>
6089  * This would consume a JSON file like this:
6090  * <pre><code>
6091 { 'results': 2, 'rows': [
6092     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6093     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6094 }
6095 </code></pre>
6096  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6097  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6098  * paged from the remote server.
6099  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6100  * @cfg {String} root name of the property which contains the Array of row objects.
6101  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6102  * @constructor
6103  * Create a new JsonReader
6104  * @param {Object} meta Metadata configuration options
6105  * @param {Object} recordType Either an Array of field definition objects,
6106  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6107  */
6108 Roo.data.JsonReader = function(meta, recordType){
6109     
6110     meta = meta || {};
6111     // set some defaults:
6112     Roo.applyIf(meta, {
6113         totalProperty: 'total',
6114         successProperty : 'success',
6115         root : 'data',
6116         id : 'id'
6117     });
6118     
6119     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6120 };
6121 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6122     
6123     /**
6124      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6125      * Used by Store query builder to append _requestMeta to params.
6126      * 
6127      */
6128     metaFromRemote : false,
6129     /**
6130      * This method is only used by a DataProxy which has retrieved data from a remote server.
6131      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6132      * @return {Object} data A data block which is used by an Roo.data.Store object as
6133      * a cache of Roo.data.Records.
6134      */
6135     read : function(response){
6136         var json = response.responseText;
6137        
6138         var o = /* eval:var:o */ eval("("+json+")");
6139         if(!o) {
6140             throw {message: "JsonReader.read: Json object not found"};
6141         }
6142         
6143         if(o.metaData){
6144             
6145             delete this.ef;
6146             this.metaFromRemote = true;
6147             this.meta = o.metaData;
6148             this.recordType = Roo.data.Record.create(o.metaData.fields);
6149             this.onMetaChange(this.meta, this.recordType, o);
6150         }
6151         return this.readRecords(o);
6152     },
6153
6154     // private function a store will implement
6155     onMetaChange : function(meta, recordType, o){
6156
6157     },
6158
6159     /**
6160          * @ignore
6161          */
6162     simpleAccess: function(obj, subsc) {
6163         return obj[subsc];
6164     },
6165
6166         /**
6167          * @ignore
6168          */
6169     getJsonAccessor: function(){
6170         var re = /[\[\.]/;
6171         return function(expr) {
6172             try {
6173                 return(re.test(expr))
6174                     ? new Function("obj", "return obj." + expr)
6175                     : function(obj){
6176                         return obj[expr];
6177                     };
6178             } catch(e){}
6179             return Roo.emptyFn;
6180         };
6181     }(),
6182
6183     /**
6184      * Create a data block containing Roo.data.Records from an XML document.
6185      * @param {Object} o An object which contains an Array of row objects in the property specified
6186      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6187      * which contains the total size of the dataset.
6188      * @return {Object} data A data block which is used by an Roo.data.Store object as
6189      * a cache of Roo.data.Records.
6190      */
6191     readRecords : function(o){
6192         /**
6193          * After any data loads, the raw JSON data is available for further custom processing.
6194          * @type Object
6195          */
6196         this.jsonData = o;
6197         var s = this.meta, Record = this.recordType,
6198             f = Record.prototype.fields, fi = f.items, fl = f.length;
6199
6200 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6201         if (!this.ef) {
6202             if(s.totalProperty) {
6203                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6204                 }
6205                 if(s.successProperty) {
6206                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6207                 }
6208                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6209                 if (s.id) {
6210                         var g = this.getJsonAccessor(s.id);
6211                         this.getId = function(rec) {
6212                                 var r = g(rec);
6213                                 return (r === undefined || r === "") ? null : r;
6214                         };
6215                 } else {
6216                         this.getId = function(){return null;};
6217                 }
6218             this.ef = [];
6219             for(var jj = 0; jj < fl; jj++){
6220                 f = fi[jj];
6221                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6222                 this.ef[jj] = this.getJsonAccessor(map);
6223             }
6224         }
6225
6226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6227         if(s.totalProperty){
6228             var vt = parseInt(this.getTotal(o), 10);
6229             if(!isNaN(vt)){
6230                 totalRecords = vt;
6231             }
6232         }
6233         if(s.successProperty){
6234             var vs = this.getSuccess(o);
6235             if(vs === false || vs === 'false'){
6236                 success = false;
6237             }
6238         }
6239         var records = [];
6240             for(var i = 0; i < c; i++){
6241                     var n = root[i];
6242                 var values = {};
6243                 var id = this.getId(n);
6244                 for(var j = 0; j < fl; j++){
6245                     f = fi[j];
6246                 var v = this.ef[j](n);
6247                 if (!f.convert) {
6248                     Roo.log('missing convert for ' + f.name);
6249                     Roo.log(f);
6250                     continue;
6251                 }
6252                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6253                 }
6254                 var record = new Record(values, id);
6255                 record.json = n;
6256                 records[i] = record;
6257             }
6258             return {
6259                 success : success,
6260                 records : records,
6261                 totalRecords : totalRecords
6262             };
6263     }
6264 });/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.data.XmlReader
6277  * @extends Roo.data.DataReader
6278  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6279  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6280  * <p>
6281  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6282  * header in the HTTP response must be set to "text/xml".</em>
6283  * <p>
6284  * Example code:
6285  * <pre><code>
6286 var RecordDef = Roo.data.Record.create([
6287    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6288    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6289 ]);
6290 var myReader = new Roo.data.XmlReader({
6291    totalRecords: "results", // The element which contains the total dataset size (optional)
6292    record: "row",           // The repeated element which contains row information
6293    id: "id"                 // The element within the row that provides an ID for the record (optional)
6294 }, RecordDef);
6295 </code></pre>
6296  * <p>
6297  * This would consume an XML file like this:
6298  * <pre><code>
6299 &lt;?xml?>
6300 &lt;dataset>
6301  &lt;results>2&lt;/results>
6302  &lt;row>
6303    &lt;id>1&lt;/id>
6304    &lt;name>Bill&lt;/name>
6305    &lt;occupation>Gardener&lt;/occupation>
6306  &lt;/row>
6307  &lt;row>
6308    &lt;id>2&lt;/id>
6309    &lt;name>Ben&lt;/name>
6310    &lt;occupation>Horticulturalist&lt;/occupation>
6311  &lt;/row>
6312 &lt;/dataset>
6313 </code></pre>
6314  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6315  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6316  * paged from the remote server.
6317  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6318  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6319  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6320  * a record identifier value.
6321  * @constructor
6322  * Create a new XmlReader
6323  * @param {Object} meta Metadata configuration options
6324  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6325  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6326  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6327  */
6328 Roo.data.XmlReader = function(meta, recordType){
6329     meta = meta || {};
6330     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6331 };
6332 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6333     /**
6334      * This method is only used by a DataProxy which has retrieved data from a remote server.
6335          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6336          * to contain a method called 'responseXML' that returns an XML document object.
6337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6338      * a cache of Roo.data.Records.
6339      */
6340     read : function(response){
6341         var doc = response.responseXML;
6342         if(!doc) {
6343             throw {message: "XmlReader.read: XML Document not available"};
6344         }
6345         return this.readRecords(doc);
6346     },
6347
6348     /**
6349      * Create a data block containing Roo.data.Records from an XML document.
6350          * @param {Object} doc A parsed XML document.
6351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6352      * a cache of Roo.data.Records.
6353      */
6354     readRecords : function(doc){
6355         /**
6356          * After any data loads/reads, the raw XML Document is available for further custom processing.
6357          * @type XMLDocument
6358          */
6359         this.xmlData = doc;
6360         var root = doc.documentElement || doc;
6361         var q = Roo.DomQuery;
6362         var recordType = this.recordType, fields = recordType.prototype.fields;
6363         var sid = this.meta.id;
6364         var totalRecords = 0, success = true;
6365         if(this.meta.totalRecords){
6366             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6367         }
6368         
6369         if(this.meta.success){
6370             var sv = q.selectValue(this.meta.success, root, true);
6371             success = sv !== false && sv !== 'false';
6372         }
6373         var records = [];
6374         var ns = q.select(this.meta.record, root);
6375         for(var i = 0, len = ns.length; i < len; i++) {
6376                 var n = ns[i];
6377                 var values = {};
6378                 var id = sid ? q.selectValue(sid, n) : undefined;
6379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6380                     var f = fields.items[j];
6381                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6382                     v = f.convert(v);
6383                     values[f.name] = v;
6384                 }
6385                 var record = new recordType(values, id);
6386                 record.node = n;
6387                 records[records.length] = record;
6388             }
6389
6390             return {
6391                 success : success,
6392                 records : records,
6393                 totalRecords : totalRecords || records.length
6394             };
6395     }
6396 });/*
6397  * Based on:
6398  * Ext JS Library 1.1.1
6399  * Copyright(c) 2006-2007, Ext JS, LLC.
6400  *
6401  * Originally Released Under LGPL - original licence link has changed is not relivant.
6402  *
6403  * Fork - LGPL
6404  * <script type="text/javascript">
6405  */
6406
6407 /**
6408  * @class Roo.data.ArrayReader
6409  * @extends Roo.data.DataReader
6410  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6411  * Each element of that Array represents a row of data fields. The
6412  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6413  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6414  * <p>
6415  * Example code:.
6416  * <pre><code>
6417 var RecordDef = Roo.data.Record.create([
6418     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6419     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6420 ]);
6421 var myReader = new Roo.data.ArrayReader({
6422     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6423 }, RecordDef);
6424 </code></pre>
6425  * <p>
6426  * This would consume an Array like this:
6427  * <pre><code>
6428 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6429   </code></pre>
6430  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6431  * @constructor
6432  * Create a new JsonReader
6433  * @param {Object} meta Metadata configuration options.
6434  * @param {Object} recordType Either an Array of field definition objects
6435  * as specified to {@link Roo.data.Record#create},
6436  * or an {@link Roo.data.Record} object
6437  * created using {@link Roo.data.Record#create}.
6438  */
6439 Roo.data.ArrayReader = function(meta, recordType){
6440     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6441 };
6442
6443 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6444     /**
6445      * Create a data block containing Roo.data.Records from an XML document.
6446      * @param {Object} o An Array of row objects which represents the dataset.
6447      * @return {Object} data A data block which is used by an Roo.data.Store object as
6448      * a cache of Roo.data.Records.
6449      */
6450     readRecords : function(o){
6451         var sid = this.meta ? this.meta.id : null;
6452         var recordType = this.recordType, fields = recordType.prototype.fields;
6453         var records = [];
6454         var root = o;
6455             for(var i = 0; i < root.length; i++){
6456                     var n = root[i];
6457                 var values = {};
6458                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6459                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6460                 var f = fields.items[j];
6461                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6462                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6463                 v = f.convert(v);
6464                 values[f.name] = v;
6465             }
6466                 var record = new recordType(values, id);
6467                 record.json = n;
6468                 records[records.length] = record;
6469             }
6470             return {
6471                 records : records,
6472                 totalRecords : records.length
6473             };
6474     }
6475 });/*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.Tree
6489  * @extends Roo.util.Observable
6490  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6491  * in the tree have most standard DOM functionality.
6492  * @constructor
6493  * @param {Node} root (optional) The root node
6494  */
6495 Roo.data.Tree = function(root){
6496    this.nodeHash = {};
6497    /**
6498     * The root node for this tree
6499     * @type Node
6500     */
6501    this.root = null;
6502    if(root){
6503        this.setRootNode(root);
6504    }
6505    this.addEvents({
6506        /**
6507         * @event append
6508         * Fires when a new child node is appended to a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The newly appended node
6512         * @param {Number} index The index of the newly appended node
6513         */
6514        "append" : true,
6515        /**
6516         * @event remove
6517         * Fires when a child node is removed from a node in this tree.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node removed
6521         */
6522        "remove" : true,
6523        /**
6524         * @event move
6525         * Fires when a node is moved to a new location in the tree
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} node The node moved
6528         * @param {Node} oldParent The old parent of this node
6529         * @param {Node} newParent The new parent of this node
6530         * @param {Number} index The index it was moved to
6531         */
6532        "move" : true,
6533        /**
6534         * @event insert
6535         * Fires when a new child node is inserted in a node in this tree.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node inserted
6539         * @param {Node} refNode The child node the node was inserted before
6540         */
6541        "insert" : true,
6542        /**
6543         * @event beforeappend
6544         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} parent The parent node
6547         * @param {Node} node The child node to be appended
6548         */
6549        "beforeappend" : true,
6550        /**
6551         * @event beforeremove
6552         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6553         * @param {Tree} tree The owner tree
6554         * @param {Node} parent The parent node
6555         * @param {Node} node The child node to be removed
6556         */
6557        "beforeremove" : true,
6558        /**
6559         * @event beforemove
6560         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} node The node being moved
6563         * @param {Node} oldParent The parent of the node
6564         * @param {Node} newParent The new parent the node is moving to
6565         * @param {Number} index The index it is being moved to
6566         */
6567        "beforemove" : true,
6568        /**
6569         * @event beforeinsert
6570         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6571         * @param {Tree} tree The owner tree
6572         * @param {Node} parent The parent node
6573         * @param {Node} node The child node to be inserted
6574         * @param {Node} refNode The child node the node is being inserted before
6575         */
6576        "beforeinsert" : true
6577    });
6578
6579     Roo.data.Tree.superclass.constructor.call(this);
6580 };
6581
6582 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6583     pathSeparator: "/",
6584
6585     proxyNodeEvent : function(){
6586         return this.fireEvent.apply(this, arguments);
6587     },
6588
6589     /**
6590      * Returns the root node for this tree.
6591      * @return {Node}
6592      */
6593     getRootNode : function(){
6594         return this.root;
6595     },
6596
6597     /**
6598      * Sets the root node for this tree.
6599      * @param {Node} node
6600      * @return {Node}
6601      */
6602     setRootNode : function(node){
6603         this.root = node;
6604         node.ownerTree = this;
6605         node.isRoot = true;
6606         this.registerNode(node);
6607         return node;
6608     },
6609
6610     /**
6611      * Gets a node in this tree by its id.
6612      * @param {String} id
6613      * @return {Node}
6614      */
6615     getNodeById : function(id){
6616         return this.nodeHash[id];
6617     },
6618
6619     registerNode : function(node){
6620         this.nodeHash[node.id] = node;
6621     },
6622
6623     unregisterNode : function(node){
6624         delete this.nodeHash[node.id];
6625     },
6626
6627     toString : function(){
6628         return "[Tree"+(this.id?" "+this.id:"")+"]";
6629     }
6630 });
6631
6632 /**
6633  * @class Roo.data.Node
6634  * @extends Roo.util.Observable
6635  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6636  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6637  * @constructor
6638  * @param {Object} attributes The attributes/config for the node
6639  */
6640 Roo.data.Node = function(attributes){
6641     /**
6642      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6643      * @type {Object}
6644      */
6645     this.attributes = attributes || {};
6646     this.leaf = this.attributes.leaf;
6647     /**
6648      * The node id. @type String
6649      */
6650     this.id = this.attributes.id;
6651     if(!this.id){
6652         this.id = Roo.id(null, "ynode-");
6653         this.attributes.id = this.id;
6654     }
6655     /**
6656      * All child nodes of this node. @type Array
6657      */
6658     this.childNodes = [];
6659     if(!this.childNodes.indexOf){ // indexOf is a must
6660         this.childNodes.indexOf = function(o){
6661             for(var i = 0, len = this.length; i < len; i++){
6662                 if(this[i] == o) {
6663                     return i;
6664                 }
6665             }
6666             return -1;
6667         };
6668     }
6669     /**
6670      * The parent node for this node. @type Node
6671      */
6672     this.parentNode = null;
6673     /**
6674      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.firstChild = null;
6677     /**
6678      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6679      */
6680     this.lastChild = null;
6681     /**
6682      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.previousSibling = null;
6685     /**
6686      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6687      */
6688     this.nextSibling = null;
6689
6690     this.addEvents({
6691        /**
6692         * @event append
6693         * Fires when a new child node is appended
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The newly appended node
6697         * @param {Number} index The index of the newly appended node
6698         */
6699        "append" : true,
6700        /**
6701         * @event remove
6702         * Fires when a child node is removed
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The removed node
6706         */
6707        "remove" : true,
6708        /**
6709         * @event move
6710         * Fires when this node is moved to a new location in the tree
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} oldParent The old parent of this node
6714         * @param {Node} newParent The new parent of this node
6715         * @param {Number} index The index it was moved to
6716         */
6717        "move" : true,
6718        /**
6719         * @event insert
6720         * Fires when a new child node is inserted.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node inserted
6724         * @param {Node} refNode The child node the node was inserted before
6725         */
6726        "insert" : true,
6727        /**
6728         * @event beforeappend
6729         * Fires before a new child is appended, return false to cancel the append.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The child node to be appended
6733         */
6734        "beforeappend" : true,
6735        /**
6736         * @event beforeremove
6737         * Fires before a child is removed, return false to cancel the remove.
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} node The child node to be removed
6741         */
6742        "beforeremove" : true,
6743        /**
6744         * @event beforemove
6745         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} oldParent The parent of this node
6749         * @param {Node} newParent The new parent this node is moving to
6750         * @param {Number} index The index it is being moved to
6751         */
6752        "beforemove" : true,
6753        /**
6754         * @event beforeinsert
6755         * Fires before a new child is inserted, return false to cancel the insert.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be inserted
6759         * @param {Node} refNode The child node the node is being inserted before
6760         */
6761        "beforeinsert" : true
6762    });
6763     this.listeners = this.attributes.listeners;
6764     Roo.data.Node.superclass.constructor.call(this);
6765 };
6766
6767 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6768     fireEvent : function(evtName){
6769         // first do standard event for this node
6770         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6771             return false;
6772         }
6773         // then bubble it up to the tree if the event wasn't cancelled
6774         var ot = this.getOwnerTree();
6775         if(ot){
6776             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6777                 return false;
6778             }
6779         }
6780         return true;
6781     },
6782
6783     /**
6784      * Returns true if this node is a leaf
6785      * @return {Boolean}
6786      */
6787     isLeaf : function(){
6788         return this.leaf === true;
6789     },
6790
6791     // private
6792     setFirstChild : function(node){
6793         this.firstChild = node;
6794     },
6795
6796     //private
6797     setLastChild : function(node){
6798         this.lastChild = node;
6799     },
6800
6801
6802     /**
6803      * Returns true if this node is the last child of its parent
6804      * @return {Boolean}
6805      */
6806     isLast : function(){
6807        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6808     },
6809
6810     /**
6811      * Returns true if this node is the first child of its parent
6812      * @return {Boolean}
6813      */
6814     isFirst : function(){
6815        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6816     },
6817
6818     hasChildNodes : function(){
6819         return !this.isLeaf() && this.childNodes.length > 0;
6820     },
6821
6822     /**
6823      * Insert node(s) as the last child node of this node.
6824      * @param {Node/Array} node The node or Array of nodes to append
6825      * @return {Node} The appended node if single append, or null if an array was passed
6826      */
6827     appendChild : function(node){
6828         var multi = false;
6829         if(node instanceof Array){
6830             multi = node;
6831         }else if(arguments.length > 1){
6832             multi = arguments;
6833         }
6834         // if passed an array or multiple args do them one by one
6835         if(multi){
6836             for(var i = 0, len = multi.length; i < len; i++) {
6837                 this.appendChild(multi[i]);
6838             }
6839         }else{
6840             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6841                 return false;
6842             }
6843             var index = this.childNodes.length;
6844             var oldParent = node.parentNode;
6845             // it's a move, make sure we move it cleanly
6846             if(oldParent){
6847                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6848                     return false;
6849                 }
6850                 oldParent.removeChild(node);
6851             }
6852             index = this.childNodes.length;
6853             if(index == 0){
6854                 this.setFirstChild(node);
6855             }
6856             this.childNodes.push(node);
6857             node.parentNode = this;
6858             var ps = this.childNodes[index-1];
6859             if(ps){
6860                 node.previousSibling = ps;
6861                 ps.nextSibling = node;
6862             }else{
6863                 node.previousSibling = null;
6864             }
6865             node.nextSibling = null;
6866             this.setLastChild(node);
6867             node.setOwnerTree(this.getOwnerTree());
6868             this.fireEvent("append", this.ownerTree, this, node, index);
6869             if(oldParent){
6870                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6871             }
6872             return node;
6873         }
6874     },
6875
6876     /**
6877      * Removes a child node from this node.
6878      * @param {Node} node The node to remove
6879      * @return {Node} The removed node
6880      */
6881     removeChild : function(node){
6882         var index = this.childNodes.indexOf(node);
6883         if(index == -1){
6884             return false;
6885         }
6886         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6887             return false;
6888         }
6889
6890         // remove it from childNodes collection
6891         this.childNodes.splice(index, 1);
6892
6893         // update siblings
6894         if(node.previousSibling){
6895             node.previousSibling.nextSibling = node.nextSibling;
6896         }
6897         if(node.nextSibling){
6898             node.nextSibling.previousSibling = node.previousSibling;
6899         }
6900
6901         // update child refs
6902         if(this.firstChild == node){
6903             this.setFirstChild(node.nextSibling);
6904         }
6905         if(this.lastChild == node){
6906             this.setLastChild(node.previousSibling);
6907         }
6908
6909         node.setOwnerTree(null);
6910         // clear any references from the node
6911         node.parentNode = null;
6912         node.previousSibling = null;
6913         node.nextSibling = null;
6914         this.fireEvent("remove", this.ownerTree, this, node);
6915         return node;
6916     },
6917
6918     /**
6919      * Inserts the first node before the second node in this nodes childNodes collection.
6920      * @param {Node} node The node to insert
6921      * @param {Node} refNode The node to insert before (if null the node is appended)
6922      * @return {Node} The inserted node
6923      */
6924     insertBefore : function(node, refNode){
6925         if(!refNode){ // like standard Dom, refNode can be null for append
6926             return this.appendChild(node);
6927         }
6928         // nothing to do
6929         if(node == refNode){
6930             return false;
6931         }
6932
6933         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6934             return false;
6935         }
6936         var index = this.childNodes.indexOf(refNode);
6937         var oldParent = node.parentNode;
6938         var refIndex = index;
6939
6940         // when moving internally, indexes will change after remove
6941         if(oldParent == this && this.childNodes.indexOf(node) < index){
6942             refIndex--;
6943         }
6944
6945         // it's a move, make sure we move it cleanly
6946         if(oldParent){
6947             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6948                 return false;
6949             }
6950             oldParent.removeChild(node);
6951         }
6952         if(refIndex == 0){
6953             this.setFirstChild(node);
6954         }
6955         this.childNodes.splice(refIndex, 0, node);
6956         node.parentNode = this;
6957         var ps = this.childNodes[refIndex-1];
6958         if(ps){
6959             node.previousSibling = ps;
6960             ps.nextSibling = node;
6961         }else{
6962             node.previousSibling = null;
6963         }
6964         node.nextSibling = refNode;
6965         refNode.previousSibling = node;
6966         node.setOwnerTree(this.getOwnerTree());
6967         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6968         if(oldParent){
6969             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6970         }
6971         return node;
6972     },
6973
6974     /**
6975      * Returns the child node at the specified index.
6976      * @param {Number} index
6977      * @return {Node}
6978      */
6979     item : function(index){
6980         return this.childNodes[index];
6981     },
6982
6983     /**
6984      * Replaces one child node in this node with another.
6985      * @param {Node} newChild The replacement node
6986      * @param {Node} oldChild The node to replace
6987      * @return {Node} The replaced node
6988      */
6989     replaceChild : function(newChild, oldChild){
6990         this.insertBefore(newChild, oldChild);
6991         this.removeChild(oldChild);
6992         return oldChild;
6993     },
6994
6995     /**
6996      * Returns the index of a child node
6997      * @param {Node} node
6998      * @return {Number} The index of the node or -1 if it was not found
6999      */
7000     indexOf : function(child){
7001         return this.childNodes.indexOf(child);
7002     },
7003
7004     /**
7005      * Returns the tree this node is in.
7006      * @return {Tree}
7007      */
7008     getOwnerTree : function(){
7009         // if it doesn't have one, look for one
7010         if(!this.ownerTree){
7011             var p = this;
7012             while(p){
7013                 if(p.ownerTree){
7014                     this.ownerTree = p.ownerTree;
7015                     break;
7016                 }
7017                 p = p.parentNode;
7018             }
7019         }
7020         return this.ownerTree;
7021     },
7022
7023     /**
7024      * Returns depth of this node (the root node has a depth of 0)
7025      * @return {Number}
7026      */
7027     getDepth : function(){
7028         var depth = 0;
7029         var p = this;
7030         while(p.parentNode){
7031             ++depth;
7032             p = p.parentNode;
7033         }
7034         return depth;
7035     },
7036
7037     // private
7038     setOwnerTree : function(tree){
7039         // if it's move, we need to update everyone
7040         if(tree != this.ownerTree){
7041             if(this.ownerTree){
7042                 this.ownerTree.unregisterNode(this);
7043             }
7044             this.ownerTree = tree;
7045             var cs = this.childNodes;
7046             for(var i = 0, len = cs.length; i < len; i++) {
7047                 cs[i].setOwnerTree(tree);
7048             }
7049             if(tree){
7050                 tree.registerNode(this);
7051             }
7052         }
7053     },
7054
7055     /**
7056      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7057      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7058      * @return {String} The path
7059      */
7060     getPath : function(attr){
7061         attr = attr || "id";
7062         var p = this.parentNode;
7063         var b = [this.attributes[attr]];
7064         while(p){
7065             b.unshift(p.attributes[attr]);
7066             p = p.parentNode;
7067         }
7068         var sep = this.getOwnerTree().pathSeparator;
7069         return sep + b.join(sep);
7070     },
7071
7072     /**
7073      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7074      * function call will be the scope provided or the current node. The arguments to the function
7075      * will be the args provided or the current node. If the function returns false at any point,
7076      * the bubble is stopped.
7077      * @param {Function} fn The function to call
7078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7080      */
7081     bubble : function(fn, scope, args){
7082         var p = this;
7083         while(p){
7084             if(fn.call(scope || p, args || p) === false){
7085                 break;
7086             }
7087             p = p.parentNode;
7088         }
7089     },
7090
7091     /**
7092      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the cascade is stopped on that branch.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     cascade : function(fn, scope, args){
7101         if(fn.call(scope || this, args || this) !== false){
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].cascade(fn, scope, args);
7105             }
7106         }
7107     },
7108
7109     /**
7110      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7111      * function call will be the scope provided or the current node. The arguments to the function
7112      * will be the args provided or the current node. If the function returns false at any point,
7113      * the iteration stops.
7114      * @param {Function} fn The function to call
7115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7117      */
7118     eachChild : function(fn, scope, args){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(fn.call(scope || this, args || cs[i]) === false){
7122                     break;
7123                 }
7124         }
7125     },
7126
7127     /**
7128      * Finds the first child that has the attribute with the specified value.
7129      * @param {String} attribute The attribute name
7130      * @param {Mixed} value The value to search for
7131      * @return {Node} The found child or null if none was found
7132      */
7133     findChild : function(attribute, value){
7134         var cs = this.childNodes;
7135         for(var i = 0, len = cs.length; i < len; i++) {
7136                 if(cs[i].attributes[attribute] == value){
7137                     return cs[i];
7138                 }
7139         }
7140         return null;
7141     },
7142
7143     /**
7144      * Finds the first child by a custom function. The child matches if the function passed
7145      * returns true.
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      * @return {Node} The found child or null if none was found
7149      */
7150     findChildBy : function(fn, scope){
7151         var cs = this.childNodes;
7152         for(var i = 0, len = cs.length; i < len; i++) {
7153                 if(fn.call(scope||cs[i], cs[i]) === true){
7154                     return cs[i];
7155                 }
7156         }
7157         return null;
7158     },
7159
7160     /**
7161      * Sorts this nodes children using the supplied sort function
7162      * @param {Function} fn
7163      * @param {Object} scope (optional)
7164      */
7165     sort : function(fn, scope){
7166         var cs = this.childNodes;
7167         var len = cs.length;
7168         if(len > 0){
7169             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7170             cs.sort(sortFn);
7171             for(var i = 0; i < len; i++){
7172                 var n = cs[i];
7173                 n.previousSibling = cs[i-1];
7174                 n.nextSibling = cs[i+1];
7175                 if(i == 0){
7176                     this.setFirstChild(n);
7177                 }
7178                 if(i == len-1){
7179                     this.setLastChild(n);
7180                 }
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Returns true if this node is an ancestor (at any point) of the passed node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     contains : function(node){
7191         return node.isAncestor(this);
7192     },
7193
7194     /**
7195      * Returns true if the passed node is an ancestor (at any point) of this node.
7196      * @param {Node} node
7197      * @return {Boolean}
7198      */
7199     isAncestor : function(node){
7200         var p = this.parentNode;
7201         while(p){
7202             if(p == node){
7203                 return true;
7204             }
7205             p = p.parentNode;
7206         }
7207         return false;
7208     },
7209
7210     toString : function(){
7211         return "[Node"+(this.id?" "+this.id:"")+"]";
7212     }
7213 });/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224
7225 /**
7226  * @class Roo.ComponentMgr
7227  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7228  * @singleton
7229  */
7230 Roo.ComponentMgr = function(){
7231     var all = new Roo.util.MixedCollection();
7232
7233     return {
7234         /**
7235          * Registers a component.
7236          * @param {Roo.Component} c The component
7237          */
7238         register : function(c){
7239             all.add(c);
7240         },
7241
7242         /**
7243          * Unregisters a component.
7244          * @param {Roo.Component} c The component
7245          */
7246         unregister : function(c){
7247             all.remove(c);
7248         },
7249
7250         /**
7251          * Returns a component by id
7252          * @param {String} id The component id
7253          */
7254         get : function(id){
7255             return all.get(id);
7256         },
7257
7258         /**
7259          * Registers a function that will be called when a specified component is added to ComponentMgr
7260          * @param {String} id The component id
7261          * @param {Funtction} fn The callback function
7262          * @param {Object} scope The scope of the callback
7263          */
7264         onAvailable : function(id, fn, scope){
7265             all.on("add", function(index, o){
7266                 if(o.id == id){
7267                     fn.call(scope || o, o);
7268                     all.un("add", fn, scope);
7269                 }
7270             });
7271         }
7272     };
7273 }();/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284 /**
7285  * @class Roo.Component
7286  * @extends Roo.util.Observable
7287  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7288  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7289  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7290  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7291  * All visual components (widgets) that require rendering into a layout should subclass Component.
7292  * @constructor
7293  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7294  * 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
7295  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7296  */
7297 Roo.Component = function(config){
7298     config = config || {};
7299     if(config.tagName || config.dom || typeof config == "string"){ // element object
7300         config = {el: config, id: config.id || config};
7301     }
7302     this.initialConfig = config;
7303
7304     Roo.apply(this, config);
7305     this.addEvents({
7306         /**
7307          * @event disable
7308          * Fires after the component is disabled.
7309              * @param {Roo.Component} this
7310              */
7311         disable : true,
7312         /**
7313          * @event enable
7314          * Fires after the component is enabled.
7315              * @param {Roo.Component} this
7316              */
7317         enable : true,
7318         /**
7319          * @event beforeshow
7320          * Fires before the component is shown.  Return false to stop the show.
7321              * @param {Roo.Component} this
7322              */
7323         beforeshow : true,
7324         /**
7325          * @event show
7326          * Fires after the component is shown.
7327              * @param {Roo.Component} this
7328              */
7329         show : true,
7330         /**
7331          * @event beforehide
7332          * Fires before the component is hidden. Return false to stop the hide.
7333              * @param {Roo.Component} this
7334              */
7335         beforehide : true,
7336         /**
7337          * @event hide
7338          * Fires after the component is hidden.
7339              * @param {Roo.Component} this
7340              */
7341         hide : true,
7342         /**
7343          * @event beforerender
7344          * Fires before the component is rendered. Return false to stop the render.
7345              * @param {Roo.Component} this
7346              */
7347         beforerender : true,
7348         /**
7349          * @event render
7350          * Fires after the component is rendered.
7351              * @param {Roo.Component} this
7352              */
7353         render : true,
7354         /**
7355          * @event beforedestroy
7356          * Fires before the component is destroyed. Return false to stop the destroy.
7357              * @param {Roo.Component} this
7358              */
7359         beforedestroy : true,
7360         /**
7361          * @event destroy
7362          * Fires after the component is destroyed.
7363              * @param {Roo.Component} this
7364              */
7365         destroy : true
7366     });
7367     if(!this.id){
7368         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7369     }
7370     Roo.ComponentMgr.register(this);
7371     Roo.Component.superclass.constructor.call(this);
7372     this.initComponent();
7373     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7374         this.render(this.renderTo);
7375         delete this.renderTo;
7376     }
7377 };
7378
7379 // private
7380 Roo.Component.AUTO_ID = 1000;
7381
7382 Roo.extend(Roo.Component, Roo.util.Observable, {
7383     /**
7384      * @property {Boolean} hidden
7385      * true if this component is hidden. Read-only.
7386      */
7387     hidden : false,
7388     /**
7389      * true if this component is disabled. Read-only.
7390      */
7391     disabled : false,
7392     /**
7393      * true if this component has been rendered. Read-only.
7394      */
7395     rendered : false,
7396     
7397     /** @cfg {String} disableClass
7398      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7399      */
7400     disabledClass : "x-item-disabled",
7401         /** @cfg {Boolean} allowDomMove
7402          * Whether the component can move the Dom node when rendering (defaults to true).
7403          */
7404     allowDomMove : true,
7405     /** @cfg {String} hideMode
7406      * How this component should hidden. Supported values are
7407      * "visibility" (css visibility), "offsets" (negative offset position) and
7408      * "display" (css display) - defaults to "display".
7409      */
7410     hideMode: 'display',
7411
7412     // private
7413     ctype : "Roo.Component",
7414
7415     /** @cfg {String} actionMode 
7416      * which property holds the element that used for  hide() / show() / disable() / enable()
7417      * default is 'el' 
7418      */
7419     actionMode : "el",
7420
7421     // private
7422     getActionEl : function(){
7423         return this[this.actionMode];
7424     },
7425
7426     initComponent : Roo.emptyFn,
7427     /**
7428      * If this is a lazy rendering component, render it to its container element.
7429      * @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.
7430      */
7431     render : function(container, position){
7432         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7433             if(!container && this.el){
7434                 this.el = Roo.get(this.el);
7435                 container = this.el.dom.parentNode;
7436                 this.allowDomMove = false;
7437             }
7438             this.container = Roo.get(container);
7439             this.rendered = true;
7440             if(position !== undefined){
7441                 if(typeof position == 'number'){
7442                     position = this.container.dom.childNodes[position];
7443                 }else{
7444                     position = Roo.getDom(position);
7445                 }
7446             }
7447             this.onRender(this.container, position || null);
7448             if(this.cls){
7449                 this.el.addClass(this.cls);
7450                 delete this.cls;
7451             }
7452             if(this.style){
7453                 this.el.applyStyles(this.style);
7454                 delete this.style;
7455             }
7456             this.fireEvent("render", this);
7457             this.afterRender(this.container);
7458             if(this.hidden){
7459                 this.hide();
7460             }
7461             if(this.disabled){
7462                 this.disable();
7463             }
7464         }
7465         return this;
7466     },
7467
7468     // private
7469     // default function is not really useful
7470     onRender : function(ct, position){
7471         if(this.el){
7472             this.el = Roo.get(this.el);
7473             if(this.allowDomMove !== false){
7474                 ct.dom.insertBefore(this.el.dom, position);
7475             }
7476         }
7477     },
7478
7479     // private
7480     getAutoCreate : function(){
7481         var cfg = typeof this.autoCreate == "object" ?
7482                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7483         if(this.id && !cfg.id){
7484             cfg.id = this.id;
7485         }
7486         return cfg;
7487     },
7488
7489     // private
7490     afterRender : Roo.emptyFn,
7491
7492     /**
7493      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7494      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7495      */
7496     destroy : function(){
7497         if(this.fireEvent("beforedestroy", this) !== false){
7498             this.purgeListeners();
7499             this.beforeDestroy();
7500             if(this.rendered){
7501                 this.el.removeAllListeners();
7502                 this.el.remove();
7503                 if(this.actionMode == "container"){
7504                     this.container.remove();
7505                 }
7506             }
7507             this.onDestroy();
7508             Roo.ComponentMgr.unregister(this);
7509             this.fireEvent("destroy", this);
7510         }
7511     },
7512
7513         // private
7514     beforeDestroy : function(){
7515
7516     },
7517
7518         // private
7519         onDestroy : function(){
7520
7521     },
7522
7523     /**
7524      * Returns the underlying {@link Roo.Element}.
7525      * @return {Roo.Element} The element
7526      */
7527     getEl : function(){
7528         return this.el;
7529     },
7530
7531     /**
7532      * Returns the id of this component.
7533      * @return {String}
7534      */
7535     getId : function(){
7536         return this.id;
7537     },
7538
7539     /**
7540      * Try to focus this component.
7541      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7542      * @return {Roo.Component} this
7543      */
7544     focus : function(selectText){
7545         if(this.rendered){
7546             this.el.focus();
7547             if(selectText === true){
7548                 this.el.dom.select();
7549             }
7550         }
7551         return this;
7552     },
7553
7554     // private
7555     blur : function(){
7556         if(this.rendered){
7557             this.el.blur();
7558         }
7559         return this;
7560     },
7561
7562     /**
7563      * Disable this component.
7564      * @return {Roo.Component} this
7565      */
7566     disable : function(){
7567         if(this.rendered){
7568             this.onDisable();
7569         }
7570         this.disabled = true;
7571         this.fireEvent("disable", this);
7572         return this;
7573     },
7574
7575         // private
7576     onDisable : function(){
7577         this.getActionEl().addClass(this.disabledClass);
7578         this.el.dom.disabled = true;
7579     },
7580
7581     /**
7582      * Enable this component.
7583      * @return {Roo.Component} this
7584      */
7585     enable : function(){
7586         if(this.rendered){
7587             this.onEnable();
7588         }
7589         this.disabled = false;
7590         this.fireEvent("enable", this);
7591         return this;
7592     },
7593
7594         // private
7595     onEnable : function(){
7596         this.getActionEl().removeClass(this.disabledClass);
7597         this.el.dom.disabled = false;
7598     },
7599
7600     /**
7601      * Convenience function for setting disabled/enabled by boolean.
7602      * @param {Boolean} disabled
7603      */
7604     setDisabled : function(disabled){
7605         this[disabled ? "disable" : "enable"]();
7606     },
7607
7608     /**
7609      * Show this component.
7610      * @return {Roo.Component} this
7611      */
7612     show: function(){
7613         if(this.fireEvent("beforeshow", this) !== false){
7614             this.hidden = false;
7615             if(this.rendered){
7616                 this.onShow();
7617             }
7618             this.fireEvent("show", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onShow : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "visible";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.removeClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "";
7632         }
7633     },
7634
7635     /**
7636      * Hide this component.
7637      * @return {Roo.Component} this
7638      */
7639     hide: function(){
7640         if(this.fireEvent("beforehide", this) !== false){
7641             this.hidden = true;
7642             if(this.rendered){
7643                 this.onHide();
7644             }
7645             this.fireEvent("hide", this);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     onHide : function(){
7652         var ae = this.getActionEl();
7653         if(this.hideMode == 'visibility'){
7654             ae.dom.style.visibility = "hidden";
7655         }else if(this.hideMode == 'offsets'){
7656             ae.addClass('x-hidden');
7657         }else{
7658             ae.dom.style.display = "none";
7659         }
7660     },
7661
7662     /**
7663      * Convenience function to hide or show this component by boolean.
7664      * @param {Boolean} visible True to show, false to hide
7665      * @return {Roo.Component} this
7666      */
7667     setVisible: function(visible){
7668         if(visible) {
7669             this.show();
7670         }else{
7671             this.hide();
7672         }
7673         return this;
7674     },
7675
7676     /**
7677      * Returns true if this component is visible.
7678      */
7679     isVisible : function(){
7680         return this.getActionEl().isVisible();
7681     },
7682
7683     cloneConfig : function(overrides){
7684         overrides = overrides || {};
7685         var id = overrides.id || Roo.id();
7686         var cfg = Roo.applyIf(overrides, this.initialConfig);
7687         cfg.id = id; // prevent dup id
7688         return new this.constructor(cfg);
7689     }
7690 });/*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700  (function(){ 
7701 /**
7702  * @class Roo.Layer
7703  * @extends Roo.Element
7704  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7705  * automatic maintaining of shadow/shim positions.
7706  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7707  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7708  * you can pass a string with a CSS class name. False turns off the shadow.
7709  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7710  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7711  * @cfg {String} cls CSS class to add to the element
7712  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7713  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7714  * @constructor
7715  * @param {Object} config An object with config options.
7716  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7717  */
7718
7719 Roo.Layer = function(config, existingEl){
7720     config = config || {};
7721     var dh = Roo.DomHelper;
7722     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7723     if(existingEl){
7724         this.dom = Roo.getDom(existingEl);
7725     }
7726     if(!this.dom){
7727         var o = config.dh || {tag: "div", cls: "x-layer"};
7728         this.dom = dh.append(pel, o);
7729     }
7730     if(config.cls){
7731         this.addClass(config.cls);
7732     }
7733     this.constrain = config.constrain !== false;
7734     this.visibilityMode = Roo.Element.VISIBILITY;
7735     if(config.id){
7736         this.id = this.dom.id = config.id;
7737     }else{
7738         this.id = Roo.id(this.dom);
7739     }
7740     this.zindex = config.zindex || this.getZIndex();
7741     this.position("absolute", this.zindex);
7742     if(config.shadow){
7743         this.shadowOffset = config.shadowOffset || 4;
7744         this.shadow = new Roo.Shadow({
7745             offset : this.shadowOffset,
7746             mode : config.shadow
7747         });
7748     }else{
7749         this.shadowOffset = 0;
7750     }
7751     this.useShim = config.shim !== false && Roo.useShims;
7752     this.useDisplay = config.useDisplay;
7753     this.hide();
7754 };
7755
7756 var supr = Roo.Element.prototype;
7757
7758 // shims are shared among layer to keep from having 100 iframes
7759 var shims = [];
7760
7761 Roo.extend(Roo.Layer, Roo.Element, {
7762
7763     getZIndex : function(){
7764         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7765     },
7766
7767     getShim : function(){
7768         if(!this.useShim){
7769             return null;
7770         }
7771         if(this.shim){
7772             return this.shim;
7773         }
7774         var shim = shims.shift();
7775         if(!shim){
7776             shim = this.createShim();
7777             shim.enableDisplayMode('block');
7778             shim.dom.style.display = 'none';
7779             shim.dom.style.visibility = 'visible';
7780         }
7781         var pn = this.dom.parentNode;
7782         if(shim.dom.parentNode != pn){
7783             pn.insertBefore(shim.dom, this.dom);
7784         }
7785         shim.setStyle('z-index', this.getZIndex()-2);
7786         this.shim = shim;
7787         return shim;
7788     },
7789
7790     hideShim : function(){
7791         if(this.shim){
7792             this.shim.setDisplayed(false);
7793             shims.push(this.shim);
7794             delete this.shim;
7795         }
7796     },
7797
7798     disableShadow : function(){
7799         if(this.shadow){
7800             this.shadowDisabled = true;
7801             this.shadow.hide();
7802             this.lastShadowOffset = this.shadowOffset;
7803             this.shadowOffset = 0;
7804         }
7805     },
7806
7807     enableShadow : function(show){
7808         if(this.shadow){
7809             this.shadowDisabled = false;
7810             this.shadowOffset = this.lastShadowOffset;
7811             delete this.lastShadowOffset;
7812             if(show){
7813                 this.sync(true);
7814             }
7815         }
7816     },
7817
7818     // private
7819     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7820     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7821     sync : function(doShow){
7822         var sw = this.shadow;
7823         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7824             var sh = this.getShim();
7825
7826             var w = this.getWidth(),
7827                 h = this.getHeight();
7828
7829             var l = this.getLeft(true),
7830                 t = this.getTop(true);
7831
7832             if(sw && !this.shadowDisabled){
7833                 if(doShow && !sw.isVisible()){
7834                     sw.show(this);
7835                 }else{
7836                     sw.realign(l, t, w, h);
7837                 }
7838                 if(sh){
7839                     if(doShow){
7840                        sh.show();
7841                     }
7842                     // fit the shim behind the shadow, so it is shimmed too
7843                     var a = sw.adjusts, s = sh.dom.style;
7844                     s.left = (Math.min(l, l+a.l))+"px";
7845                     s.top = (Math.min(t, t+a.t))+"px";
7846                     s.width = (w+a.w)+"px";
7847                     s.height = (h+a.h)+"px";
7848                 }
7849             }else if(sh){
7850                 if(doShow){
7851                    sh.show();
7852                 }
7853                 sh.setSize(w, h);
7854                 sh.setLeftTop(l, t);
7855             }
7856             
7857         }
7858     },
7859
7860     // private
7861     destroy : function(){
7862         this.hideShim();
7863         if(this.shadow){
7864             this.shadow.hide();
7865         }
7866         this.removeAllListeners();
7867         var pn = this.dom.parentNode;
7868         if(pn){
7869             pn.removeChild(this.dom);
7870         }
7871         Roo.Element.uncache(this.id);
7872     },
7873
7874     remove : function(){
7875         this.destroy();
7876     },
7877
7878     // private
7879     beginUpdate : function(){
7880         this.updating = true;
7881     },
7882
7883     // private
7884     endUpdate : function(){
7885         this.updating = false;
7886         this.sync(true);
7887     },
7888
7889     // private
7890     hideUnders : function(negOffset){
7891         if(this.shadow){
7892             this.shadow.hide();
7893         }
7894         this.hideShim();
7895     },
7896
7897     // private
7898     constrainXY : function(){
7899         if(this.constrain){
7900             var vw = Roo.lib.Dom.getViewWidth(),
7901                 vh = Roo.lib.Dom.getViewHeight();
7902             var s = Roo.get(document).getScroll();
7903
7904             var xy = this.getXY();
7905             var x = xy[0], y = xy[1];   
7906             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7907             // only move it if it needs it
7908             var moved = false;
7909             // first validate right/bottom
7910             if((x + w) > vw+s.left){
7911                 x = vw - w - this.shadowOffset;
7912                 moved = true;
7913             }
7914             if((y + h) > vh+s.top){
7915                 y = vh - h - this.shadowOffset;
7916                 moved = true;
7917             }
7918             // then make sure top/left isn't negative
7919             if(x < s.left){
7920                 x = s.left;
7921                 moved = true;
7922             }
7923             if(y < s.top){
7924                 y = s.top;
7925                 moved = true;
7926             }
7927             if(moved){
7928                 if(this.avoidY){
7929                     var ay = this.avoidY;
7930                     if(y <= ay && (y+h) >= ay){
7931                         y = ay-h-5;   
7932                     }
7933                 }
7934                 xy = [x, y];
7935                 this.storeXY(xy);
7936                 supr.setXY.call(this, xy);
7937                 this.sync();
7938             }
7939         }
7940     },
7941
7942     isVisible : function(){
7943         return this.visible;    
7944     },
7945
7946     // private
7947     showAction : function(){
7948         this.visible = true; // track visibility to prevent getStyle calls
7949         if(this.useDisplay === true){
7950             this.setDisplayed("");
7951         }else if(this.lastXY){
7952             supr.setXY.call(this, this.lastXY);
7953         }else if(this.lastLT){
7954             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7955         }
7956     },
7957
7958     // private
7959     hideAction : function(){
7960         this.visible = false;
7961         if(this.useDisplay === true){
7962             this.setDisplayed(false);
7963         }else{
7964             this.setLeftTop(-10000,-10000);
7965         }
7966     },
7967
7968     // overridden Element method
7969     setVisible : function(v, a, d, c, e){
7970         if(v){
7971             this.showAction();
7972         }
7973         if(a && v){
7974             var cb = function(){
7975                 this.sync(true);
7976                 if(c){
7977                     c();
7978                 }
7979             }.createDelegate(this);
7980             supr.setVisible.call(this, true, true, d, cb, e);
7981         }else{
7982             if(!v){
7983                 this.hideUnders(true);
7984             }
7985             var cb = c;
7986             if(a){
7987                 cb = function(){
7988                     this.hideAction();
7989                     if(c){
7990                         c();
7991                     }
7992                 }.createDelegate(this);
7993             }
7994             supr.setVisible.call(this, v, a, d, cb, e);
7995             if(v){
7996                 this.sync(true);
7997             }else if(!a){
7998                 this.hideAction();
7999             }
8000         }
8001     },
8002
8003     storeXY : function(xy){
8004         delete this.lastLT;
8005         this.lastXY = xy;
8006     },
8007
8008     storeLeftTop : function(left, top){
8009         delete this.lastXY;
8010         this.lastLT = [left, top];
8011     },
8012
8013     // private
8014     beforeFx : function(){
8015         this.beforeAction();
8016         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8017     },
8018
8019     // private
8020     afterFx : function(){
8021         Roo.Layer.superclass.afterFx.apply(this, arguments);
8022         this.sync(this.isVisible());
8023     },
8024
8025     // private
8026     beforeAction : function(){
8027         if(!this.updating && this.shadow){
8028             this.shadow.hide();
8029         }
8030     },
8031
8032     // overridden Element method
8033     setLeft : function(left){
8034         this.storeLeftTop(left, this.getTop(true));
8035         supr.setLeft.apply(this, arguments);
8036         this.sync();
8037     },
8038
8039     setTop : function(top){
8040         this.storeLeftTop(this.getLeft(true), top);
8041         supr.setTop.apply(this, arguments);
8042         this.sync();
8043     },
8044
8045     setLeftTop : function(left, top){
8046         this.storeLeftTop(left, top);
8047         supr.setLeftTop.apply(this, arguments);
8048         this.sync();
8049     },
8050
8051     setXY : function(xy, a, d, c, e){
8052         this.fixDisplay();
8053         this.beforeAction();
8054         this.storeXY(xy);
8055         var cb = this.createCB(c);
8056         supr.setXY.call(this, xy, a, d, cb, e);
8057         if(!a){
8058             cb();
8059         }
8060     },
8061
8062     // private
8063     createCB : function(c){
8064         var el = this;
8065         return function(){
8066             el.constrainXY();
8067             el.sync(true);
8068             if(c){
8069                 c();
8070             }
8071         };
8072     },
8073
8074     // overridden Element method
8075     setX : function(x, a, d, c, e){
8076         this.setXY([x, this.getY()], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setY : function(y, a, d, c, e){
8081         this.setXY([this.getX(), y], a, d, c, e);
8082     },
8083
8084     // overridden Element method
8085     setSize : function(w, h, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setSize.call(this, w, h, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setWidth : function(w, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setWidth.call(this, w, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setHeight : function(h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         supr.setHeight.call(this, h, a, d, cb, e);
8109         if(!a){
8110             cb();
8111         }
8112     },
8113
8114     // overridden Element method
8115     setBounds : function(x, y, w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         if(!a){
8119             this.storeXY([x, y]);
8120             supr.setXY.call(this, [x, y]);
8121             supr.setSize.call(this, w, h, a, d, cb, e);
8122             cb();
8123         }else{
8124             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8125         }
8126         return this;
8127     },
8128     
8129     /**
8130      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8131      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8132      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8133      * @param {Number} zindex The new z-index to set
8134      * @return {this} The Layer
8135      */
8136     setZIndex : function(zindex){
8137         this.zindex = zindex;
8138         this.setStyle("z-index", zindex + 2);
8139         if(this.shadow){
8140             this.shadow.setZIndex(zindex + 1);
8141         }
8142         if(this.shim){
8143             this.shim.setStyle("z-index", zindex);
8144         }
8145     }
8146 });
8147 })();/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158
8159 /**
8160  * @class Roo.Shadow
8161  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8162  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8163  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8164  * @constructor
8165  * Create a new Shadow
8166  * @param {Object} config The config object
8167  */
8168 Roo.Shadow = function(config){
8169     Roo.apply(this, config);
8170     if(typeof this.mode != "string"){
8171         this.mode = this.defaultMode;
8172     }
8173     var o = this.offset, a = {h: 0};
8174     var rad = Math.floor(this.offset/2);
8175     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8176         case "drop":
8177             a.w = 0;
8178             a.l = a.t = o;
8179             a.t -= 1;
8180             if(Roo.isIE){
8181                 a.l -= this.offset + rad;
8182                 a.t -= this.offset + rad;
8183                 a.w -= rad;
8184                 a.h -= rad;
8185                 a.t += 1;
8186             }
8187         break;
8188         case "sides":
8189             a.w = (o*2);
8190             a.l = -o;
8191             a.t = o-1;
8192             if(Roo.isIE){
8193                 a.l -= (this.offset - rad);
8194                 a.t -= this.offset + rad;
8195                 a.l += 1;
8196                 a.w -= (this.offset - rad)*2;
8197                 a.w -= rad + 1;
8198                 a.h -= 1;
8199             }
8200         break;
8201         case "frame":
8202             a.w = a.h = (o*2);
8203             a.l = a.t = -o;
8204             a.t += 1;
8205             a.h -= 2;
8206             if(Roo.isIE){
8207                 a.l -= (this.offset - rad);
8208                 a.t -= (this.offset - rad);
8209                 a.l += 1;
8210                 a.w -= (this.offset + rad + 1);
8211                 a.h -= (this.offset + rad);
8212                 a.h += 1;
8213             }
8214         break;
8215     };
8216
8217     this.adjusts = a;
8218 };
8219
8220 Roo.Shadow.prototype = {
8221     /**
8222      * @cfg {String} mode
8223      * The shadow display mode.  Supports the following options:<br />
8224      * sides: Shadow displays on both sides and bottom only<br />
8225      * frame: Shadow displays equally on all four sides<br />
8226      * drop: Traditional bottom-right drop shadow (default)
8227      */
8228     /**
8229      * @cfg {String} offset
8230      * The number of pixels to offset the shadow from the element (defaults to 4)
8231      */
8232     offset: 4,
8233
8234     // private
8235     defaultMode: "drop",
8236
8237     /**
8238      * Displays the shadow under the target element
8239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8240      */
8241     show : function(target){
8242         target = Roo.get(target);
8243         if(!this.el){
8244             this.el = Roo.Shadow.Pool.pull();
8245             if(this.el.dom.nextSibling != target.dom){
8246                 this.el.insertBefore(target);
8247             }
8248         }
8249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8250         if(Roo.isIE){
8251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8252         }
8253         this.realign(
8254             target.getLeft(true),
8255             target.getTop(true),
8256             target.getWidth(),
8257             target.getHeight()
8258         );
8259         this.el.dom.style.display = "block";
8260     },
8261
8262     /**
8263      * Returns true if the shadow is visible, else false
8264      */
8265     isVisible : function(){
8266         return this.el ? true : false;  
8267     },
8268
8269     /**
8270      * Direct alignment when values are already available. Show must be called at least once before
8271      * calling this method to ensure it is initialized.
8272      * @param {Number} left The target element left position
8273      * @param {Number} top The target element top position
8274      * @param {Number} width The target element width
8275      * @param {Number} height The target element height
8276      */
8277     realign : function(l, t, w, h){
8278         if(!this.el){
8279             return;
8280         }
8281         var a = this.adjusts, d = this.el.dom, s = d.style;
8282         var iea = 0;
8283         s.left = (l+a.l)+"px";
8284         s.top = (t+a.t)+"px";
8285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8286  
8287         if(s.width != sws || s.height != shs){
8288             s.width = sws;
8289             s.height = shs;
8290             if(!Roo.isIE){
8291                 var cn = d.childNodes;
8292                 var sww = Math.max(0, (sw-12))+"px";
8293                 cn[0].childNodes[1].style.width = sww;
8294                 cn[1].childNodes[1].style.width = sww;
8295                 cn[2].childNodes[1].style.width = sww;
8296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8297             }
8298         }
8299     },
8300
8301     /**
8302      * Hides this shadow
8303      */
8304     hide : function(){
8305         if(this.el){
8306             this.el.dom.style.display = "none";
8307             Roo.Shadow.Pool.push(this.el);
8308             delete this.el;
8309         }
8310     },
8311
8312     /**
8313      * Adjust the z-index of this shadow
8314      * @param {Number} zindex The new z-index
8315      */
8316     setZIndex : function(z){
8317         this.zIndex = z;
8318         if(this.el){
8319             this.el.setStyle("z-index", z);
8320         }
8321     }
8322 };
8323
8324 // Private utility class that manages the internal Shadow cache
8325 Roo.Shadow.Pool = function(){
8326     var p = [];
8327     var markup = Roo.isIE ?
8328                  '<div class="x-ie-shadow"></div>' :
8329                  '<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>';
8330     return {
8331         pull : function(){
8332             var sh = p.shift();
8333             if(!sh){
8334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8335                 sh.autoBoxAdjust = false;
8336             }
8337             return sh;
8338         },
8339
8340         push : function(sh){
8341             p.push(sh);
8342         }
8343     };
8344 }();/*
8345  * Based on:
8346  * Ext JS Library 1.1.1
8347  * Copyright(c) 2006-2007, Ext JS, LLC.
8348  *
8349  * Originally Released Under LGPL - original licence link has changed is not relivant.
8350  *
8351  * Fork - LGPL
8352  * <script type="text/javascript">
8353  */
8354
8355 /**
8356  * @class Roo.BoxComponent
8357  * @extends Roo.Component
8358  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8359  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8360  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8361  * layout containers.
8362  * @constructor
8363  * @param {Roo.Element/String/Object} config The configuration options.
8364  */
8365 Roo.BoxComponent = function(config){
8366     Roo.Component.call(this, config);
8367     this.addEvents({
8368         /**
8369          * @event resize
8370          * Fires after the component is resized.
8371              * @param {Roo.Component} this
8372              * @param {Number} adjWidth The box-adjusted width that was set
8373              * @param {Number} adjHeight The box-adjusted height that was set
8374              * @param {Number} rawWidth The width that was originally specified
8375              * @param {Number} rawHeight The height that was originally specified
8376              */
8377         resize : true,
8378         /**
8379          * @event move
8380          * Fires after the component is moved.
8381              * @param {Roo.Component} this
8382              * @param {Number} x The new x position
8383              * @param {Number} y The new y position
8384              */
8385         move : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.BoxComponent, Roo.Component, {
8390     // private, set in afterRender to signify that the component has been rendered
8391     boxReady : false,
8392     // private, used to defer height settings to subclasses
8393     deferHeight: false,
8394     /** @cfg {Number} width
8395      * width (optional) size of component
8396      */
8397      /** @cfg {Number} height
8398      * height (optional) size of component
8399      */
8400      
8401     /**
8402      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8403      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8404      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8405      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8406      * @return {Roo.BoxComponent} this
8407      */
8408     setSize : function(w, h){
8409         // support for standard size objects
8410         if(typeof w == 'object'){
8411             h = w.height;
8412             w = w.width;
8413         }
8414         // not rendered
8415         if(!this.boxReady){
8416             this.width = w;
8417             this.height = h;
8418             return this;
8419         }
8420
8421         // prevent recalcs when not needed
8422         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8423             return this;
8424         }
8425         this.lastSize = {width: w, height: h};
8426
8427         var adj = this.adjustSize(w, h);
8428         var aw = adj.width, ah = adj.height;
8429         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8430             var rz = this.getResizeEl();
8431             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8432                 rz.setSize(aw, ah);
8433             }else if(!this.deferHeight && ah !== undefined){
8434                 rz.setHeight(ah);
8435             }else if(aw !== undefined){
8436                 rz.setWidth(aw);
8437             }
8438             this.onResize(aw, ah, w, h);
8439             this.fireEvent('resize', this, aw, ah, w, h);
8440         }
8441         return this;
8442     },
8443
8444     /**
8445      * Gets the current size of the component's underlying element.
8446      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8447      */
8448     getSize : function(){
8449         return this.el.getSize();
8450     },
8451
8452     /**
8453      * Gets the current XY position of the component's underlying element.
8454      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8455      * @return {Array} The XY position of the element (e.g., [100, 200])
8456      */
8457     getPosition : function(local){
8458         if(local === true){
8459             return [this.el.getLeft(true), this.el.getTop(true)];
8460         }
8461         return this.xy || this.el.getXY();
8462     },
8463
8464     /**
8465      * Gets the current box measurements of the component's underlying element.
8466      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8467      * @returns {Object} box An object in the format {x, y, width, height}
8468      */
8469     getBox : function(local){
8470         var s = this.el.getSize();
8471         if(local){
8472             s.x = this.el.getLeft(true);
8473             s.y = this.el.getTop(true);
8474         }else{
8475             var xy = this.xy || this.el.getXY();
8476             s.x = xy[0];
8477             s.y = xy[1];
8478         }
8479         return s;
8480     },
8481
8482     /**
8483      * Sets the current box measurements of the component's underlying element.
8484      * @param {Object} box An object in the format {x, y, width, height}
8485      * @returns {Roo.BoxComponent} this
8486      */
8487     updateBox : function(box){
8488         this.setSize(box.width, box.height);
8489         this.setPagePosition(box.x, box.y);
8490         return this;
8491     },
8492
8493     // protected
8494     getResizeEl : function(){
8495         return this.resizeEl || this.el;
8496     },
8497
8498     // protected
8499     getPositionEl : function(){
8500         return this.positionEl || this.el;
8501     },
8502
8503     /**
8504      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8505      * This method fires the move event.
8506      * @param {Number} left The new left
8507      * @param {Number} top The new top
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     setPosition : function(x, y){
8511         this.x = x;
8512         this.y = y;
8513         if(!this.boxReady){
8514             return this;
8515         }
8516         var adj = this.adjustPosition(x, y);
8517         var ax = adj.x, ay = adj.y;
8518
8519         var el = this.getPositionEl();
8520         if(ax !== undefined || ay !== undefined){
8521             if(ax !== undefined && ay !== undefined){
8522                 el.setLeftTop(ax, ay);
8523             }else if(ax !== undefined){
8524                 el.setLeft(ax);
8525             }else if(ay !== undefined){
8526                 el.setTop(ay);
8527             }
8528             this.onPosition(ax, ay);
8529             this.fireEvent('move', this, ax, ay);
8530         }
8531         return this;
8532     },
8533
8534     /**
8535      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8536      * This method fires the move event.
8537      * @param {Number} x The new x position
8538      * @param {Number} y The new y position
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPagePosition : function(x, y){
8542         this.pageX = x;
8543         this.pageY = y;
8544         if(!this.boxReady){
8545             return;
8546         }
8547         if(x === undefined || y === undefined){ // cannot translate undefined points
8548             return;
8549         }
8550         var p = this.el.translatePoints(x, y);
8551         this.setPosition(p.left, p.top);
8552         return this;
8553     },
8554
8555     // private
8556     onRender : function(ct, position){
8557         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8558         if(this.resizeEl){
8559             this.resizeEl = Roo.get(this.resizeEl);
8560         }
8561         if(this.positionEl){
8562             this.positionEl = Roo.get(this.positionEl);
8563         }
8564     },
8565
8566     // private
8567     afterRender : function(){
8568         Roo.BoxComponent.superclass.afterRender.call(this);
8569         this.boxReady = true;
8570         this.setSize(this.width, this.height);
8571         if(this.x || this.y){
8572             this.setPosition(this.x, this.y);
8573         }
8574         if(this.pageX || this.pageY){
8575             this.setPagePosition(this.pageX, this.pageY);
8576         }
8577     },
8578
8579     /**
8580      * Force the component's size to recalculate based on the underlying element's current height and width.
8581      * @returns {Roo.BoxComponent} this
8582      */
8583     syncSize : function(){
8584         delete this.lastSize;
8585         this.setSize(this.el.getWidth(), this.el.getHeight());
8586         return this;
8587     },
8588
8589     /**
8590      * Called after the component is resized, this method is empty by default but can be implemented by any
8591      * subclass that needs to perform custom logic after a resize occurs.
8592      * @param {Number} adjWidth The box-adjusted width that was set
8593      * @param {Number} adjHeight The box-adjusted height that was set
8594      * @param {Number} rawWidth The width that was originally specified
8595      * @param {Number} rawHeight The height that was originally specified
8596      */
8597     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8598
8599     },
8600
8601     /**
8602      * Called after the component is moved, this method is empty by default but can be implemented by any
8603      * subclass that needs to perform custom logic after a move occurs.
8604      * @param {Number} x The new x position
8605      * @param {Number} y The new y position
8606      */
8607     onPosition : function(x, y){
8608
8609     },
8610
8611     // private
8612     adjustSize : function(w, h){
8613         if(this.autoWidth){
8614             w = 'auto';
8615         }
8616         if(this.autoHeight){
8617             h = 'auto';
8618         }
8619         return {width : w, height: h};
8620     },
8621
8622     // private
8623     adjustPosition : function(x, y){
8624         return {x : x, y: y};
8625     }
8626 });/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637
8638 /**
8639  * @class Roo.SplitBar
8640  * @extends Roo.util.Observable
8641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8642  * <br><br>
8643  * Usage:
8644  * <pre><code>
8645 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8646                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8647 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8648 split.minSize = 100;
8649 split.maxSize = 600;
8650 split.animate = true;
8651 split.on('moved', splitterMoved);
8652 </code></pre>
8653  * @constructor
8654  * Create a new SplitBar
8655  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8656  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8657  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8659                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8660                         position of the SplitBar).
8661  */
8662 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8663     
8664     /** @private */
8665     this.el = Roo.get(dragElement, true);
8666     this.el.dom.unselectable = "on";
8667     /** @private */
8668     this.resizingEl = Roo.get(resizingElement, true);
8669
8670     /**
8671      * @private
8672      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8673      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8674      * @type Number
8675      */
8676     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8677     
8678     /**
8679      * The minimum size of the resizing element. (Defaults to 0)
8680      * @type Number
8681      */
8682     this.minSize = 0;
8683     
8684     /**
8685      * The maximum size of the resizing element. (Defaults to 2000)
8686      * @type Number
8687      */
8688     this.maxSize = 2000;
8689     
8690     /**
8691      * Whether to animate the transition to the new size
8692      * @type Boolean
8693      */
8694     this.animate = false;
8695     
8696     /**
8697      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8698      * @type Boolean
8699      */
8700     this.useShim = false;
8701     
8702     /** @private */
8703     this.shim = null;
8704     
8705     if(!existingProxy){
8706         /** @private */
8707         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8708     }else{
8709         this.proxy = Roo.get(existingProxy).dom;
8710     }
8711     /** @private */
8712     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8713     
8714     /** @private */
8715     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8716     
8717     /** @private */
8718     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8719     
8720     /** @private */
8721     this.dragSpecs = {};
8722     
8723     /**
8724      * @private The adapter to use to positon and resize elements
8725      */
8726     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8727     this.adapter.init(this);
8728     
8729     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8730         /** @private */
8731         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8732         this.el.addClass("x-splitbar-h");
8733     }else{
8734         /** @private */
8735         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8736         this.el.addClass("x-splitbar-v");
8737     }
8738     
8739     this.addEvents({
8740         /**
8741          * @event resize
8742          * Fires when the splitter is moved (alias for {@link #event-moved})
8743          * @param {Roo.SplitBar} this
8744          * @param {Number} newSize the new width or height
8745          */
8746         "resize" : true,
8747         /**
8748          * @event moved
8749          * Fires when the splitter is moved
8750          * @param {Roo.SplitBar} this
8751          * @param {Number} newSize the new width or height
8752          */
8753         "moved" : true,
8754         /**
8755          * @event beforeresize
8756          * Fires before the splitter is dragged
8757          * @param {Roo.SplitBar} this
8758          */
8759         "beforeresize" : true,
8760
8761         "beforeapply" : true
8762     });
8763
8764     Roo.util.Observable.call(this);
8765 };
8766
8767 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8768     onStartProxyDrag : function(x, y){
8769         this.fireEvent("beforeresize", this);
8770         if(!this.overlay){
8771             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8772             o.unselectable();
8773             o.enableDisplayMode("block");
8774             // all splitbars share the same overlay
8775             Roo.SplitBar.prototype.overlay = o;
8776         }
8777         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8778         this.overlay.show();
8779         Roo.get(this.proxy).setDisplayed("block");
8780         var size = this.adapter.getElementSize(this);
8781         this.activeMinSize = this.getMinimumSize();;
8782         this.activeMaxSize = this.getMaximumSize();;
8783         var c1 = size - this.activeMinSize;
8784         var c2 = Math.max(this.activeMaxSize - size, 0);
8785         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786             this.dd.resetConstraints();
8787             this.dd.setXConstraint(
8788                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8789                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8790             );
8791             this.dd.setYConstraint(0, 0);
8792         }else{
8793             this.dd.resetConstraints();
8794             this.dd.setXConstraint(0, 0);
8795             this.dd.setYConstraint(
8796                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8797                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8798             );
8799          }
8800         this.dragSpecs.startSize = size;
8801         this.dragSpecs.startPoint = [x, y];
8802         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8803     },
8804     
8805     /** 
8806      * @private Called after the drag operation by the DDProxy
8807      */
8808     onEndProxyDrag : function(e){
8809         Roo.get(this.proxy).setDisplayed(false);
8810         var endPoint = Roo.lib.Event.getXY(e);
8811         if(this.overlay){
8812             this.overlay.hide();
8813         }
8814         var newSize;
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             newSize = this.dragSpecs.startSize + 
8817                 (this.placement == Roo.SplitBar.LEFT ?
8818                     endPoint[0] - this.dragSpecs.startPoint[0] :
8819                     this.dragSpecs.startPoint[0] - endPoint[0]
8820                 );
8821         }else{
8822             newSize = this.dragSpecs.startSize + 
8823                 (this.placement == Roo.SplitBar.TOP ?
8824                     endPoint[1] - this.dragSpecs.startPoint[1] :
8825                     this.dragSpecs.startPoint[1] - endPoint[1]
8826                 );
8827         }
8828         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8829         if(newSize != this.dragSpecs.startSize){
8830             if(this.fireEvent('beforeapply', this, newSize) !== false){
8831                 this.adapter.setElementSize(this, newSize);
8832                 this.fireEvent("moved", this, newSize);
8833                 this.fireEvent("resize", this, newSize);
8834             }
8835         }
8836     },
8837     
8838     /**
8839      * Get the adapter this SplitBar uses
8840      * @return The adapter object
8841      */
8842     getAdapter : function(){
8843         return this.adapter;
8844     },
8845     
8846     /**
8847      * Set the adapter this SplitBar uses
8848      * @param {Object} adapter A SplitBar adapter object
8849      */
8850     setAdapter : function(adapter){
8851         this.adapter = adapter;
8852         this.adapter.init(this);
8853     },
8854     
8855     /**
8856      * Gets the minimum size for the resizing element
8857      * @return {Number} The minimum size
8858      */
8859     getMinimumSize : function(){
8860         return this.minSize;
8861     },
8862     
8863     /**
8864      * Sets the minimum size for the resizing element
8865      * @param {Number} minSize The minimum size
8866      */
8867     setMinimumSize : function(minSize){
8868         this.minSize = minSize;
8869     },
8870     
8871     /**
8872      * Gets the maximum size for the resizing element
8873      * @return {Number} The maximum size
8874      */
8875     getMaximumSize : function(){
8876         return this.maxSize;
8877     },
8878     
8879     /**
8880      * Sets the maximum size for the resizing element
8881      * @param {Number} maxSize The maximum size
8882      */
8883     setMaximumSize : function(maxSize){
8884         this.maxSize = maxSize;
8885     },
8886     
8887     /**
8888      * Sets the initialize size for the resizing element
8889      * @param {Number} size The initial size
8890      */
8891     setCurrentSize : function(size){
8892         var oldAnimate = this.animate;
8893         this.animate = false;
8894         this.adapter.setElementSize(this, size);
8895         this.animate = oldAnimate;
8896     },
8897     
8898     /**
8899      * Destroy this splitbar. 
8900      * @param {Boolean} removeEl True to remove the element
8901      */
8902     destroy : function(removeEl){
8903         if(this.shim){
8904             this.shim.remove();
8905         }
8906         this.dd.unreg();
8907         this.proxy.parentNode.removeChild(this.proxy);
8908         if(removeEl){
8909             this.el.remove();
8910         }
8911     }
8912 });
8913
8914 /**
8915  * @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.
8916  */
8917 Roo.SplitBar.createProxy = function(dir){
8918     var proxy = new Roo.Element(document.createElement("div"));
8919     proxy.unselectable();
8920     var cls = 'x-splitbar-proxy';
8921     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8922     document.body.appendChild(proxy.dom);
8923     return proxy.dom;
8924 };
8925
8926 /** 
8927  * @class Roo.SplitBar.BasicLayoutAdapter
8928  * Default Adapter. It assumes the splitter and resizing element are not positioned
8929  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8930  */
8931 Roo.SplitBar.BasicLayoutAdapter = function(){
8932 };
8933
8934 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8935     // do nothing for now
8936     init : function(s){
8937     
8938     },
8939     /**
8940      * Called before drag operations to get the current size of the resizing element. 
8941      * @param {Roo.SplitBar} s The SplitBar using this adapter
8942      */
8943      getElementSize : function(s){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             return s.resizingEl.getWidth();
8946         }else{
8947             return s.resizingEl.getHeight();
8948         }
8949     },
8950     
8951     /**
8952      * Called after drag operations to set the size of the resizing element.
8953      * @param {Roo.SplitBar} s The SplitBar using this adapter
8954      * @param {Number} newSize The new size to set
8955      * @param {Function} onComplete A function to be invoked when resizing is complete
8956      */
8957     setElementSize : function(s, newSize, onComplete){
8958         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8959             if(!s.animate){
8960                 s.resizingEl.setWidth(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }else{
8968             
8969             if(!s.animate){
8970                 s.resizingEl.setHeight(newSize);
8971                 if(onComplete){
8972                     onComplete(s, newSize);
8973                 }
8974             }else{
8975                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8976             }
8977         }
8978     }
8979 };
8980
8981 /** 
8982  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8983  * @extends Roo.SplitBar.BasicLayoutAdapter
8984  * Adapter that  moves the splitter element to align with the resized sizing element. 
8985  * Used with an absolute positioned SplitBar.
8986  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8987  * document.body, make sure you assign an id to the body element.
8988  */
8989 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8990     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8991     this.container = Roo.get(container);
8992 };
8993
8994 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8995     init : function(s){
8996         this.basic.init(s);
8997     },
8998     
8999     getElementSize : function(s){
9000         return this.basic.getElementSize(s);
9001     },
9002     
9003     setElementSize : function(s, newSize, onComplete){
9004         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9005     },
9006     
9007     moveSplitter : function(s){
9008         var yes = Roo.SplitBar;
9009         switch(s.placement){
9010             case yes.LEFT:
9011                 s.el.setX(s.resizingEl.getRight());
9012                 break;
9013             case yes.RIGHT:
9014                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9015                 break;
9016             case yes.TOP:
9017                 s.el.setY(s.resizingEl.getBottom());
9018                 break;
9019             case yes.BOTTOM:
9020                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9021                 break;
9022         }
9023     }
9024 };
9025
9026 /**
9027  * Orientation constant - Create a vertical SplitBar
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.VERTICAL = 1;
9032
9033 /**
9034  * Orientation constant - Create a horizontal SplitBar
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.HORIZONTAL = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is to the left of the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.LEFT = 1;
9046
9047 /**
9048  * Placement constant - The resizing element is to the right of the splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.RIGHT = 2;
9053
9054 /**
9055  * Placement constant - The resizing element is positioned above the splitter element
9056  * @static
9057  * @type Number
9058  */
9059 Roo.SplitBar.TOP = 3;
9060
9061 /**
9062  * Placement constant - The resizing element is positioned under splitter element
9063  * @static
9064  * @type Number
9065  */
9066 Roo.SplitBar.BOTTOM = 4;
9067 /*
9068  * Based on:
9069  * Ext JS Library 1.1.1
9070  * Copyright(c) 2006-2007, Ext JS, LLC.
9071  *
9072  * Originally Released Under LGPL - original licence link has changed is not relivant.
9073  *
9074  * Fork - LGPL
9075  * <script type="text/javascript">
9076  */
9077
9078 /**
9079  * @class Roo.View
9080  * @extends Roo.util.Observable
9081  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9082  * This class also supports single and multi selection modes. <br>
9083  * Create a data model bound view:
9084  <pre><code>
9085  var store = new Roo.data.Store(...);
9086
9087  var view = new Roo.View({
9088     el : "my-element",
9089     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9090  
9091     singleSelect: true,
9092     selectedClass: "ydataview-selected",
9093     store: store
9094  });
9095
9096  // listen for node click?
9097  view.on("click", function(vw, index, node, e){
9098  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9099  });
9100
9101  // load XML data
9102  dataModel.load("foobar.xml");
9103  </code></pre>
9104  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9105  * <br><br>
9106  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9107  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9108  * 
9109  * Note: old style constructor is still suported (container, template, config)
9110  * 
9111  * @constructor
9112  * Create a new View
9113  * @param {Object} config The config object
9114  * 
9115  */
9116 Roo.View = function(config, depreciated_tpl, depreciated_config){
9117     
9118     if (typeof(depreciated_tpl) == 'undefined') {
9119         // new way.. - universal constructor.
9120         Roo.apply(this, config);
9121         this.el  = Roo.get(this.el);
9122     } else {
9123         // old format..
9124         this.el  = Roo.get(config);
9125         this.tpl = depreciated_tpl;
9126         Roo.apply(this, depreciated_config);
9127     }
9128      
9129     
9130     if(typeof(this.tpl) == "string"){
9131         this.tpl = new Roo.Template(this.tpl);
9132     } else {
9133         // support xtype ctors..
9134         this.tpl = new Roo.factory(this.tpl, Roo);
9135     }
9136     
9137     
9138     this.tpl.compile();
9139    
9140
9141      
9142     /** @private */
9143     this.addEvents({
9144     /**
9145      * @event beforeclick
9146      * Fires before a click is processed. Returns false to cancel the default action.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "beforeclick" : true,
9153     /**
9154      * @event click
9155      * Fires when a template node is clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "click" : true,
9162     /**
9163      * @event dblclick
9164      * Fires when a template node is double clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "dblclick" : true,
9171     /**
9172      * @event contextmenu
9173      * Fires when a template node is right clicked.
9174      * @param {Roo.View} this
9175      * @param {Number} index The index of the target node
9176      * @param {HTMLElement} node The target node
9177      * @param {Roo.EventObject} e The raw event object
9178      */
9179         "contextmenu" : true,
9180     /**
9181      * @event selectionchange
9182      * Fires when the selected nodes change.
9183      * @param {Roo.View} this
9184      * @param {Array} selections Array of the selected nodes
9185      */
9186         "selectionchange" : true,
9187
9188     /**
9189      * @event beforeselect
9190      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9191      * @param {Roo.View} this
9192      * @param {HTMLElement} node The node to be selected
9193      * @param {Array} selections Array of currently selected nodes
9194      */
9195         "beforeselect" : true
9196     });
9197
9198     this.el.on({
9199         "click": this.onClick,
9200         "dblclick": this.onDblClick,
9201         "contextmenu": this.onContextMenu,
9202         scope:this
9203     });
9204
9205     this.selections = [];
9206     this.nodes = [];
9207     this.cmp = new Roo.CompositeElementLite([]);
9208     if(this.store){
9209         this.store = Roo.factory(this.store, Roo.data);
9210         this.setStore(this.store, true);
9211     }
9212     Roo.View.superclass.constructor.call(this);
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     /**
9241      * @cfg {Boolean} multiSelect Allow multiple selection
9242      */
9243     
9244     multiSelect : false,
9245     /**
9246      * @cfg {Boolean} singleSelect Allow single selection
9247      */
9248     singleSelect:  false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.el;
9256     },
9257
9258     /**
9259      * Refreshes the view.
9260      */
9261     refresh : function(){
9262         var t = this.tpl;
9263         this.clearSelections();
9264         this.el.update("");
9265         var html = [];
9266         var records = this.store.getRange();
9267         if(records.length < 1){
9268             this.el.update(this.emptyText);
9269             return;
9270         }
9271         for(var i = 0, len = records.length; i < len; i++){
9272             var data = this.prepareData(records[i].data, i, records[i]);
9273             html[html.length] = t.apply(data);
9274         }
9275         this.el.update(html.join(""));
9276         this.nodes = this.el.dom.childNodes;
9277         this.updateIndexes(0);
9278     },
9279
9280     /**
9281      * Function to override to reformat the data that is sent to
9282      * the template for each node.
9283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9284      * a JSON object for an UpdateManager bound view).
9285      */
9286     prepareData : function(data){
9287         return data;
9288     },
9289
9290     onUpdate : function(ds, record){
9291         this.clearSelections();
9292         var index = this.store.indexOf(record);
9293         var n = this.nodes[index];
9294         this.tpl.insertBefore(n, this.prepareData(record.data));
9295         n.parentNode.removeChild(n);
9296         this.updateIndexes(index, index);
9297     },
9298
9299     onAdd : function(ds, records, index){
9300         this.clearSelections();
9301         if(this.nodes.length == 0){
9302             this.refresh();
9303             return;
9304         }
9305         var n = this.nodes[index];
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var d = this.prepareData(records[i].data);
9308             if(n){
9309                 this.tpl.insertBefore(n, d);
9310             }else{
9311                 this.tpl.append(this.el, d);
9312             }
9313         }
9314         this.updateIndexes(index);
9315     },
9316
9317     onRemove : function(ds, record, index){
9318         this.clearSelections();
9319         this.el.dom.removeChild(this.nodes[index]);
9320         this.updateIndexes(index);
9321     },
9322
9323     /**
9324      * Refresh an individual node.
9325      * @param {Number} index
9326      */
9327     refreshNode : function(index){
9328         this.onUpdate(this.store, this.store.getAt(index));
9329     },
9330
9331     updateIndexes : function(startIndex, endIndex){
9332         var ns = this.nodes;
9333         startIndex = startIndex || 0;
9334         endIndex = endIndex || ns.length - 1;
9335         for(var i = startIndex; i <= endIndex; i++){
9336             ns[i].nodeIndex = i;
9337         }
9338     },
9339
9340     /**
9341      * Changes the data store this view uses and refresh the view.
9342      * @param {Store} store
9343      */
9344     setStore : function(store, initial){
9345         if(!initial && this.store){
9346             this.store.un("datachanged", this.refresh);
9347             this.store.un("add", this.onAdd);
9348             this.store.un("remove", this.onRemove);
9349             this.store.un("update", this.onUpdate);
9350             this.store.un("clear", this.refresh);
9351         }
9352         if(store){
9353           
9354             store.on("datachanged", this.refresh, this);
9355             store.on("add", this.onAdd, this);
9356             store.on("remove", this.onRemove, this);
9357             store.on("update", this.onUpdate, this);
9358             store.on("clear", this.refresh, this);
9359         }
9360         
9361         if(store){
9362             this.refresh();
9363         }
9364     },
9365
9366     /**
9367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9368      * @param {HTMLElement} node
9369      * @return {HTMLElement} The template node
9370      */
9371     findItemFromChild : function(node){
9372         var el = this.el.dom;
9373         if(!node || node.parentNode == el){
9374                     return node;
9375             }
9376             var p = node.parentNode;
9377             while(p && p != el){
9378             if(p.parentNode == el){
9379                 return p;
9380             }
9381             p = p.parentNode;
9382         }
9383             return null;
9384     },
9385
9386     /** @ignore */
9387     onClick : function(e){
9388         var item = this.findItemFromChild(e.getTarget());
9389         if(item){
9390             var index = this.indexOf(item);
9391             if(this.onItemClick(item, index, e) !== false){
9392                 this.fireEvent("click", this, index, item, e);
9393             }
9394         }else{
9395             this.clearSelections();
9396         }
9397     },
9398
9399     /** @ignore */
9400     onContextMenu : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9404         }
9405     },
9406
9407     /** @ignore */
9408     onDblClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9412         }
9413     },
9414
9415     onItemClick : function(item, index, e){
9416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9417             return false;
9418         }
9419         if(this.multiSelect || this.singleSelect){
9420             if(this.multiSelect && e.shiftKey && this.lastSelection){
9421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9422             }else{
9423                 this.select(item, this.multiSelect && e.ctrlKey);
9424                 this.lastSelection = item;
9425             }
9426             e.preventDefault();
9427         }
9428         return true;
9429     },
9430
9431     /**
9432      * Get the number of selected nodes.
9433      * @return {Number}
9434      */
9435     getSelectionCount : function(){
9436         return this.selections.length;
9437     },
9438
9439     /**
9440      * Get the currently selected nodes.
9441      * @return {Array} An array of HTMLElements
9442      */
9443     getSelectedNodes : function(){
9444         return this.selections;
9445     },
9446
9447     /**
9448      * Get the indexes of the selected nodes.
9449      * @return {Array}
9450      */
9451     getSelectedIndexes : function(){
9452         var indexes = [], s = this.selections;
9453         for(var i = 0, len = s.length; i < len; i++){
9454             indexes.push(s[i].nodeIndex);
9455         }
9456         return indexes;
9457     },
9458
9459     /**
9460      * Clear all selections
9461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9462      */
9463     clearSelections : function(suppressEvent){
9464         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9465             this.cmp.elements = this.selections;
9466             this.cmp.removeClass(this.selectedClass);
9467             this.selections = [];
9468             if(!suppressEvent){
9469                 this.fireEvent("selectionchange", this, this.selections);
9470             }
9471         }
9472     },
9473
9474     /**
9475      * Returns true if the passed node is selected
9476      * @param {HTMLElement/Number} node The node or node index
9477      * @return {Boolean}
9478      */
9479     isSelected : function(node){
9480         var s = this.selections;
9481         if(s.length < 1){
9482             return false;
9483         }
9484         node = this.getNode(node);
9485         return s.indexOf(node) !== -1;
9486     },
9487
9488     /**
9489      * Selects nodes.
9490      * @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
9491      * @param {Boolean} keepExisting (optional) true to keep existing selections
9492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9493      */
9494     select : function(nodeInfo, keepExisting, suppressEvent){
9495         if(nodeInfo instanceof Array){
9496             if(!keepExisting){
9497                 this.clearSelections(true);
9498             }
9499             for(var i = 0, len = nodeInfo.length; i < len; i++){
9500                 this.select(nodeInfo[i], true, true);
9501             }
9502         } else{
9503             var node = this.getNode(nodeInfo);
9504             if(node && !this.isSelected(node)){
9505                 if(!keepExisting){
9506                     this.clearSelections(true);
9507                 }
9508                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9509                     Roo.fly(node).addClass(this.selectedClass);
9510                     this.selections.push(node);
9511                     if(!suppressEvent){
9512                         this.fireEvent("selectionchange", this, this.selections);
9513                     }
9514                 }
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Gets a template node.
9521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9522      * @return {HTMLElement} The node or null if it wasn't found
9523      */
9524     getNode : function(nodeInfo){
9525         if(typeof nodeInfo == "string"){
9526             return document.getElementById(nodeInfo);
9527         }else if(typeof nodeInfo == "number"){
9528             return this.nodes[nodeInfo];
9529         }
9530         return nodeInfo;
9531     },
9532
9533     /**
9534      * Gets a range template nodes.
9535      * @param {Number} startIndex
9536      * @param {Number} endIndex
9537      * @return {Array} An array of nodes
9538      */
9539     getNodes : function(start, end){
9540         var ns = this.nodes;
9541         start = start || 0;
9542         end = typeof end == "undefined" ? ns.length - 1 : end;
9543         var nodes = [];
9544         if(start <= end){
9545             for(var i = start; i <= end; i++){
9546                 nodes.push(ns[i]);
9547             }
9548         } else{
9549             for(var i = start; i >= end; i--){
9550                 nodes.push(ns[i]);
9551             }
9552         }
9553         return nodes;
9554     },
9555
9556     /**
9557      * Finds the index of the passed node
9558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9559      * @return {Number} The index of the node or -1
9560      */
9561     indexOf : function(node){
9562         node = this.getNode(node);
9563         if(typeof node.nodeIndex == "number"){
9564             return node.nodeIndex;
9565         }
9566         var ns = this.nodes;
9567         for(var i = 0, len = ns.length; i < len; i++){
9568             if(ns[i] == node){
9569                 return i;
9570             }
9571         }
9572         return -1;
9573     }
9574 });
9575 /*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585
9586 /**
9587  * @class Roo.JsonView
9588  * @extends Roo.View
9589  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9590 <pre><code>
9591 var view = new Roo.JsonView({
9592     container: "my-element",
9593     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9594     multiSelect: true, 
9595     jsonRoot: "data" 
9596 });
9597
9598 // listen for node click?
9599 view.on("click", function(vw, index, node, e){
9600     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9601 });
9602
9603 // direct load of JSON data
9604 view.load("foobar.php");
9605
9606 // Example from my blog list
9607 var tpl = new Roo.Template(
9608     '&lt;div class="entry"&gt;' +
9609     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9610     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9611     "&lt;/div&gt;&lt;hr /&gt;"
9612 );
9613
9614 var moreView = new Roo.JsonView({
9615     container :  "entry-list", 
9616     template : tpl,
9617     jsonRoot: "posts"
9618 });
9619 moreView.on("beforerender", this.sortEntries, this);
9620 moreView.load({
9621     url: "/blog/get-posts.php",
9622     params: "allposts=true",
9623     text: "Loading Blog Entries..."
9624 });
9625 </code></pre>
9626
9627 * Note: old code is supported with arguments : (container, template, config)
9628
9629
9630  * @constructor
9631  * Create a new JsonView
9632  * 
9633  * @param {Object} config The config object
9634  * 
9635  */
9636 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9637     
9638     
9639     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9640
9641     var um = this.el.getUpdateManager();
9642     um.setRenderer(this);
9643     um.on("update", this.onLoad, this);
9644     um.on("failure", this.onLoadException, this);
9645
9646     /**
9647      * @event beforerender
9648      * Fires before rendering of the downloaded JSON data.
9649      * @param {Roo.JsonView} this
9650      * @param {Object} data The JSON data loaded
9651      */
9652     /**
9653      * @event load
9654      * Fires when data is loaded.
9655      * @param {Roo.JsonView} this
9656      * @param {Object} data The JSON data loaded
9657      * @param {Object} response The raw Connect response object
9658      */
9659     /**
9660      * @event loadexception
9661      * Fires when loading fails.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} response The raw Connect response object
9664      */
9665     this.addEvents({
9666         'beforerender' : true,
9667         'load' : true,
9668         'loadexception' : true
9669     });
9670 };
9671 Roo.extend(Roo.JsonView, Roo.View, {
9672     /**
9673      * @type {String} The root property in the loaded JSON object that contains the data
9674      */
9675     jsonRoot : "",
9676
9677     /**
9678      * Refreshes the view.
9679      */
9680     refresh : function(){
9681         this.clearSelections();
9682         this.el.update("");
9683         var html = [];
9684         var o = this.jsonData;
9685         if(o && o.length > 0){
9686             for(var i = 0, len = o.length; i < len; i++){
9687                 var data = this.prepareData(o[i], i, o);
9688                 html[html.length] = this.tpl.apply(data);
9689             }
9690         }else{
9691             html.push(this.emptyText);
9692         }
9693         this.el.update(html.join(""));
9694         this.nodes = this.el.dom.childNodes;
9695         this.updateIndexes(0);
9696     },
9697
9698     /**
9699      * 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.
9700      * @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:
9701      <pre><code>
9702      view.load({
9703          url: "your-url.php",
9704          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9705          callback: yourFunction,
9706          scope: yourObject, //(optional scope)
9707          discardUrl: false,
9708          nocache: false,
9709          text: "Loading...",
9710          timeout: 30,
9711          scripts: false
9712      });
9713      </code></pre>
9714      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9715      * 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.
9716      * @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}
9717      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9718      * @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.
9719      */
9720     load : function(){
9721         var um = this.el.getUpdateManager();
9722         um.update.apply(um, arguments);
9723     },
9724
9725     render : function(el, response){
9726         this.clearSelections();
9727         this.el.update("");
9728         var o;
9729         try{
9730             o = Roo.util.JSON.decode(response.responseText);
9731             if(this.jsonRoot){
9732                 
9733                 o = o[this.jsonRoot];
9734             }
9735         } catch(e){
9736         }
9737         /**
9738          * The current JSON data or null
9739          */
9740         this.jsonData = o;
9741         this.beforeRender();
9742         this.refresh();
9743     },
9744
9745 /**
9746  * Get the number of records in the current JSON dataset
9747  * @return {Number}
9748  */
9749     getCount : function(){
9750         return this.jsonData ? this.jsonData.length : 0;
9751     },
9752
9753 /**
9754  * Returns the JSON object for the specified node(s)
9755  * @param {HTMLElement/Array} node The node or an array of nodes
9756  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9757  * you get the JSON object for the node
9758  */
9759     getNodeData : function(node){
9760         if(node instanceof Array){
9761             var data = [];
9762             for(var i = 0, len = node.length; i < len; i++){
9763                 data.push(this.getNodeData(node[i]));
9764             }
9765             return data;
9766         }
9767         return this.jsonData[this.indexOf(node)] || null;
9768     },
9769
9770     beforeRender : function(){
9771         this.snapshot = this.jsonData;
9772         if(this.sortInfo){
9773             this.sort.apply(this, this.sortInfo);
9774         }
9775         this.fireEvent("beforerender", this, this.jsonData);
9776     },
9777
9778     onLoad : function(el, o){
9779         this.fireEvent("load", this, this.jsonData, o);
9780     },
9781
9782     onLoadException : function(el, o){
9783         this.fireEvent("loadexception", this, o);
9784     },
9785
9786 /**
9787  * Filter the data by a specific property.
9788  * @param {String} property A property on your JSON objects
9789  * @param {String/RegExp} value Either string that the property values
9790  * should start with, or a RegExp to test against the property
9791  */
9792     filter : function(property, value){
9793         if(this.jsonData){
9794             var data = [];
9795             var ss = this.snapshot;
9796             if(typeof value == "string"){
9797                 var vlen = value.length;
9798                 if(vlen == 0){
9799                     this.clearFilter();
9800                     return;
9801                 }
9802                 value = value.toLowerCase();
9803                 for(var i = 0, len = ss.length; i < len; i++){
9804                     var o = ss[i];
9805                     if(o[property].substr(0, vlen).toLowerCase() == value){
9806                         data.push(o);
9807                     }
9808                 }
9809             } else if(value.exec){ // regex?
9810                 for(var i = 0, len = ss.length; i < len; i++){
9811                     var o = ss[i];
9812                     if(value.test(o[property])){
9813                         data.push(o);
9814                     }
9815                 }
9816             } else{
9817                 return;
9818             }
9819             this.jsonData = data;
9820             this.refresh();
9821         }
9822     },
9823
9824 /**
9825  * Filter by a function. The passed function will be called with each
9826  * object in the current dataset. If the function returns true the value is kept,
9827  * otherwise it is filtered.
9828  * @param {Function} fn
9829  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9830  */
9831     filterBy : function(fn, scope){
9832         if(this.jsonData){
9833             var data = [];
9834             var ss = this.snapshot;
9835             for(var i = 0, len = ss.length; i < len; i++){
9836                 var o = ss[i];
9837                 if(fn.call(scope || this, o)){
9838                     data.push(o);
9839                 }
9840             }
9841             this.jsonData = data;
9842             this.refresh();
9843         }
9844     },
9845
9846 /**
9847  * Clears the current filter.
9848  */
9849     clearFilter : function(){
9850         if(this.snapshot && this.jsonData != this.snapshot){
9851             this.jsonData = this.snapshot;
9852             this.refresh();
9853         }
9854     },
9855
9856
9857 /**
9858  * Sorts the data for this view and refreshes it.
9859  * @param {String} property A property on your JSON objects to sort on
9860  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9861  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9862  */
9863     sort : function(property, dir, sortType){
9864         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9865         if(this.jsonData){
9866             var p = property;
9867             var dsc = dir && dir.toLowerCase() == "desc";
9868             var f = function(o1, o2){
9869                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9870                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9871                 ;
9872                 if(v1 < v2){
9873                     return dsc ? +1 : -1;
9874                 } else if(v1 > v2){
9875                     return dsc ? -1 : +1;
9876                 } else{
9877                     return 0;
9878                 }
9879             };
9880             this.jsonData.sort(f);
9881             this.refresh();
9882             if(this.jsonData != this.snapshot){
9883                 this.snapshot.sort(f);
9884             }
9885         }
9886     }
9887 });/*
9888  * Based on:
9889  * Ext JS Library 1.1.1
9890  * Copyright(c) 2006-2007, Ext JS, LLC.
9891  *
9892  * Originally Released Under LGPL - original licence link has changed is not relivant.
9893  *
9894  * Fork - LGPL
9895  * <script type="text/javascript">
9896  */
9897  
9898
9899 /**
9900  * @class Roo.ColorPalette
9901  * @extends Roo.Component
9902  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9903  * Here's an example of typical usage:
9904  * <pre><code>
9905 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9906 cp.render('my-div');
9907
9908 cp.on('select', function(palette, selColor){
9909     // do something with selColor
9910 });
9911 </code></pre>
9912  * @constructor
9913  * Create a new ColorPalette
9914  * @param {Object} config The config object
9915  */
9916 Roo.ColorPalette = function(config){
9917     Roo.ColorPalette.superclass.constructor.call(this, config);
9918     this.addEvents({
9919         /**
9920              * @event select
9921              * Fires when a color is selected
9922              * @param {ColorPalette} this
9923              * @param {String} color The 6-digit color hex code (without the # symbol)
9924              */
9925         select: true
9926     });
9927
9928     if(this.handler){
9929         this.on("select", this.handler, this.scope, true);
9930     }
9931 };
9932 Roo.extend(Roo.ColorPalette, Roo.Component, {
9933     /**
9934      * @cfg {String} itemCls
9935      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9936      */
9937     itemCls : "x-color-palette",
9938     /**
9939      * @cfg {String} value
9940      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9941      * the hex codes are case-sensitive.
9942      */
9943     value : null,
9944     clickEvent:'click',
9945     // private
9946     ctype: "Roo.ColorPalette",
9947
9948     /**
9949      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9950      */
9951     allowReselect : false,
9952
9953     /**
9954      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9955      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9956      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9957      * of colors with the width setting until the box is symmetrical.</p>
9958      * <p>You can override individual colors if needed:</p>
9959      * <pre><code>
9960 var cp = new Roo.ColorPalette();
9961 cp.colors[0] = "FF0000";  // change the first box to red
9962 </code></pre>
9963
9964 Or you can provide a custom array of your own for complete control:
9965 <pre><code>
9966 var cp = new Roo.ColorPalette();
9967 cp.colors = ["000000", "993300", "333300"];
9968 </code></pre>
9969      * @type Array
9970      */
9971     colors : [
9972         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9973         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9974         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9975         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9976         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9977     ],
9978
9979     // private
9980     onRender : function(container, position){
9981         var t = new Roo.MasterTemplate(
9982             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9983         );
9984         var c = this.colors;
9985         for(var i = 0, len = c.length; i < len; i++){
9986             t.add([c[i]]);
9987         }
9988         var el = document.createElement("div");
9989         el.className = this.itemCls;
9990         t.overwrite(el);
9991         container.dom.insertBefore(el, position);
9992         this.el = Roo.get(el);
9993         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9994         if(this.clickEvent != 'click'){
9995             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9996         }
9997     },
9998
9999     // private
10000     afterRender : function(){
10001         Roo.ColorPalette.superclass.afterRender.call(this);
10002         if(this.value){
10003             var s = this.value;
10004             this.value = null;
10005             this.select(s);
10006         }
10007     },
10008
10009     // private
10010     handleClick : function(e, t){
10011         e.preventDefault();
10012         if(!this.disabled){
10013             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10014             this.select(c.toUpperCase());
10015         }
10016     },
10017
10018     /**
10019      * Selects the specified color in the palette (fires the select event)
10020      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10021      */
10022     select : function(color){
10023         color = color.replace("#", "");
10024         if(color != this.value || this.allowReselect){
10025             var el = this.el;
10026             if(this.value){
10027                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10028             }
10029             el.child("a.color-"+color).addClass("x-color-palette-sel");
10030             this.value = color;
10031             this.fireEvent("select", this, color);
10032         }
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044  
10045 /**
10046  * @class Roo.DatePicker
10047  * @extends Roo.Component
10048  * Simple date picker class.
10049  * @constructor
10050  * Create a new DatePicker
10051  * @param {Object} config The config object
10052  */
10053 Roo.DatePicker = function(config){
10054     Roo.DatePicker.superclass.constructor.call(this, config);
10055
10056     this.value = config && config.value ?
10057                  config.value.clearTime() : new Date().clearTime();
10058
10059     this.addEvents({
10060         /**
10061              * @event select
10062              * Fires when a date is selected
10063              * @param {DatePicker} this
10064              * @param {Date} date The selected date
10065              */
10066         select: true
10067     });
10068
10069     if(this.handler){
10070         this.on("select", this.handler,  this.scope || this);
10071     }
10072     // build the disabledDatesRE
10073     if(!this.disabledDatesRE && this.disabledDates){
10074         var dd = this.disabledDates;
10075         var re = "(?:";
10076         for(var i = 0; i < dd.length; i++){
10077             re += dd[i];
10078             if(i != dd.length-1) re += "|";
10079         }
10080         this.disabledDatesRE = new RegExp(re + ")");
10081     }
10082 };
10083
10084 Roo.extend(Roo.DatePicker, Roo.Component, {
10085     /**
10086      * @cfg {String} todayText
10087      * The text to display on the button that selects the current date (defaults to "Today")
10088      */
10089     todayText : "Today",
10090     /**
10091      * @cfg {String} okText
10092      * The text to display on the ok button
10093      */
10094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10095     /**
10096      * @cfg {String} cancelText
10097      * The text to display on the cancel button
10098      */
10099     cancelText : "Cancel",
10100     /**
10101      * @cfg {String} todayTip
10102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10103      */
10104     todayTip : "{0} (Spacebar)",
10105     /**
10106      * @cfg {Date} minDate
10107      * Minimum allowable date (JavaScript date object, defaults to null)
10108      */
10109     minDate : null,
10110     /**
10111      * @cfg {Date} maxDate
10112      * Maximum allowable date (JavaScript date object, defaults to null)
10113      */
10114     maxDate : null,
10115     /**
10116      * @cfg {String} minText
10117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10118      */
10119     minText : "This date is before the minimum date",
10120     /**
10121      * @cfg {String} maxText
10122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10123      */
10124     maxText : "This date is after the maximum date",
10125     /**
10126      * @cfg {String} format
10127      * The default date format string which can be overriden for localization support.  The format must be
10128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10129      */
10130     format : "m/d/y",
10131     /**
10132      * @cfg {Array} disabledDays
10133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10134      */
10135     disabledDays : null,
10136     /**
10137      * @cfg {String} disabledDaysText
10138      * The tooltip to display when the date falls on a disabled day (defaults to "")
10139      */
10140     disabledDaysText : "",
10141     /**
10142      * @cfg {RegExp} disabledDatesRE
10143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10144      */
10145     disabledDatesRE : null,
10146     /**
10147      * @cfg {String} disabledDatesText
10148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10149      */
10150     disabledDatesText : "",
10151     /**
10152      * @cfg {Boolean} constrainToViewport
10153      * True to constrain the date picker to the viewport (defaults to true)
10154      */
10155     constrainToViewport : true,
10156     /**
10157      * @cfg {Array} monthNames
10158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10159      */
10160     monthNames : Date.monthNames,
10161     /**
10162      * @cfg {Array} dayNames
10163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10164      */
10165     dayNames : Date.dayNames,
10166     /**
10167      * @cfg {String} nextText
10168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10169      */
10170     nextText: 'Next Month (Control+Right)',
10171     /**
10172      * @cfg {String} prevText
10173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10174      */
10175     prevText: 'Previous Month (Control+Left)',
10176     /**
10177      * @cfg {String} monthYearText
10178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10179      */
10180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10181     /**
10182      * @cfg {Number} startDay
10183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10184      */
10185     startDay : 0,
10186     /**
10187      * @cfg {Bool} showClear
10188      * Show a clear button (usefull for date form elements that can be blank.)
10189      */
10190     
10191     showClear: false,
10192     
10193     /**
10194      * Sets the value of the date field
10195      * @param {Date} value The date to set
10196      */
10197     setValue : function(value){
10198         var old = this.value;
10199         this.value = value.clearTime(true);
10200         if(this.el){
10201             this.update(this.value);
10202         }
10203     },
10204
10205     /**
10206      * Gets the current selected value of the date field
10207      * @return {Date} The selected date
10208      */
10209     getValue : function(){
10210         return this.value;
10211     },
10212
10213     // private
10214     focus : function(){
10215         if(this.el){
10216             this.update(this.activeDate);
10217         }
10218     },
10219
10220     // private
10221     onRender : function(container, position){
10222         var m = [
10223              '<table cellspacing="0">',
10224                 '<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>',
10225                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10226         var dn = this.dayNames;
10227         for(var i = 0; i < 7; i++){
10228             var d = this.startDay+i;
10229             if(d > 6){
10230                 d = d-7;
10231             }
10232             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10233         }
10234         m[m.length] = "</tr></thead><tbody><tr>";
10235         for(var i = 0; i < 42; i++) {
10236             if(i % 7 == 0 && i != 0){
10237                 m[m.length] = "</tr><tr>";
10238             }
10239             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10240         }
10241         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10242             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10243
10244         var el = document.createElement("div");
10245         el.className = "x-date-picker";
10246         el.innerHTML = m.join("");
10247
10248         container.dom.insertBefore(el, position);
10249
10250         this.el = Roo.get(el);
10251         this.eventEl = Roo.get(el.firstChild);
10252
10253         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10254             handler: this.showPrevMonth,
10255             scope: this,
10256             preventDefault:true,
10257             stopDefault:true
10258         });
10259
10260         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10261             handler: this.showNextMonth,
10262             scope: this,
10263             preventDefault:true,
10264             stopDefault:true
10265         });
10266
10267         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10268
10269         this.monthPicker = this.el.down('div.x-date-mp');
10270         this.monthPicker.enableDisplayMode('block');
10271         
10272         var kn = new Roo.KeyNav(this.eventEl, {
10273             "left" : function(e){
10274                 e.ctrlKey ?
10275                     this.showPrevMonth() :
10276                     this.update(this.activeDate.add("d", -1));
10277             },
10278
10279             "right" : function(e){
10280                 e.ctrlKey ?
10281                     this.showNextMonth() :
10282                     this.update(this.activeDate.add("d", 1));
10283             },
10284
10285             "up" : function(e){
10286                 e.ctrlKey ?
10287                     this.showNextYear() :
10288                     this.update(this.activeDate.add("d", -7));
10289             },
10290
10291             "down" : function(e){
10292                 e.ctrlKey ?
10293                     this.showPrevYear() :
10294                     this.update(this.activeDate.add("d", 7));
10295             },
10296
10297             "pageUp" : function(e){
10298                 this.showNextMonth();
10299             },
10300
10301             "pageDown" : function(e){
10302                 this.showPrevMonth();
10303             },
10304
10305             "enter" : function(e){
10306                 e.stopPropagation();
10307                 return true;
10308             },
10309
10310             scope : this
10311         });
10312
10313         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10314
10315         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10316
10317         this.el.unselectable();
10318         
10319         this.cells = this.el.select("table.x-date-inner tbody td");
10320         this.textNodes = this.el.query("table.x-date-inner tbody span");
10321
10322         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10323             text: "&#160;",
10324             tooltip: this.monthYearText
10325         });
10326
10327         this.mbtn.on('click', this.showMonthPicker, this);
10328         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10329
10330
10331         var today = (new Date()).dateFormat(this.format);
10332         
10333         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10334         if (this.showClear) {
10335             baseTb.add( new Roo.Toolbar.Fill());
10336         }
10337         baseTb.add({
10338             text: String.format(this.todayText, today),
10339             tooltip: String.format(this.todayTip, today),
10340             handler: this.selectToday,
10341             scope: this
10342         });
10343         
10344         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10345             
10346         //});
10347         if (this.showClear) {
10348             
10349             baseTb.add( new Roo.Toolbar.Fill());
10350             baseTb.add({
10351                 text: '&#160;',
10352                 cls: 'x-btn-icon x-btn-clear',
10353                 handler: function() {
10354                     //this.value = '';
10355                     this.fireEvent("select", this, '');
10356                 },
10357                 scope: this
10358             });
10359         }
10360         
10361         
10362         if(Roo.isIE){
10363             this.el.repaint();
10364         }
10365         this.update(this.value);
10366     },
10367
10368     createMonthPicker : function(){
10369         if(!this.monthPicker.dom.firstChild){
10370             var buf = ['<table border="0" cellspacing="0">'];
10371             for(var i = 0; i < 6; i++){
10372                 buf.push(
10373                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10374                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10375                     i == 0 ?
10376                     '<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>' :
10377                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10378                 );
10379             }
10380             buf.push(
10381                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10382                     this.okText,
10383                     '</button><button type="button" class="x-date-mp-cancel">',
10384                     this.cancelText,
10385                     '</button></td></tr>',
10386                 '</table>'
10387             );
10388             this.monthPicker.update(buf.join(''));
10389             this.monthPicker.on('click', this.onMonthClick, this);
10390             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10391
10392             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10393             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10394
10395             this.mpMonths.each(function(m, a, i){
10396                 i += 1;
10397                 if((i%2) == 0){
10398                     m.dom.xmonth = 5 + Math.round(i * .5);
10399                 }else{
10400                     m.dom.xmonth = Math.round((i-1) * .5);
10401                 }
10402             });
10403         }
10404     },
10405
10406     showMonthPicker : function(){
10407         this.createMonthPicker();
10408         var size = this.el.getSize();
10409         this.monthPicker.setSize(size);
10410         this.monthPicker.child('table').setSize(size);
10411
10412         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10413         this.updateMPMonth(this.mpSelMonth);
10414         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10415         this.updateMPYear(this.mpSelYear);
10416
10417         this.monthPicker.slideIn('t', {duration:.2});
10418     },
10419
10420     updateMPYear : function(y){
10421         this.mpyear = y;
10422         var ys = this.mpYears.elements;
10423         for(var i = 1; i <= 10; i++){
10424             var td = ys[i-1], y2;
10425             if((i%2) == 0){
10426                 y2 = y + Math.round(i * .5);
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }else{
10430                 y2 = y - (5-Math.round(i * .5));
10431                 td.firstChild.innerHTML = y2;
10432                 td.xyear = y2;
10433             }
10434             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10435         }
10436     },
10437
10438     updateMPMonth : function(sm){
10439         this.mpMonths.each(function(m, a, i){
10440             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10441         });
10442     },
10443
10444     selectMPMonth: function(m){
10445         
10446     },
10447
10448     onMonthClick : function(e, t){
10449         e.stopEvent();
10450         var el = new Roo.Element(t), pn;
10451         if(el.is('button.x-date-mp-cancel')){
10452             this.hideMonthPicker();
10453         }
10454         else if(el.is('button.x-date-mp-ok')){
10455             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10456             this.hideMonthPicker();
10457         }
10458         else if(pn = el.up('td.x-date-mp-month', 2)){
10459             this.mpMonths.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelMonth = pn.dom.xmonth;
10462         }
10463         else if(pn = el.up('td.x-date-mp-year', 2)){
10464             this.mpYears.removeClass('x-date-mp-sel');
10465             pn.addClass('x-date-mp-sel');
10466             this.mpSelYear = pn.dom.xyear;
10467         }
10468         else if(el.is('a.x-date-mp-prev')){
10469             this.updateMPYear(this.mpyear-10);
10470         }
10471         else if(el.is('a.x-date-mp-next')){
10472             this.updateMPYear(this.mpyear+10);
10473         }
10474     },
10475
10476     onMonthDblClick : function(e, t){
10477         e.stopEvent();
10478         var el = new Roo.Element(t), pn;
10479         if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483         else if(pn = el.up('td.x-date-mp-year', 2)){
10484             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10485             this.hideMonthPicker();
10486         }
10487     },
10488
10489     hideMonthPicker : function(disableAnim){
10490         if(this.monthPicker){
10491             if(disableAnim === true){
10492                 this.monthPicker.hide();
10493             }else{
10494                 this.monthPicker.slideOut('t', {duration:.2});
10495             }
10496         }
10497     },
10498
10499     // private
10500     showPrevMonth : function(e){
10501         this.update(this.activeDate.add("mo", -1));
10502     },
10503
10504     // private
10505     showNextMonth : function(e){
10506         this.update(this.activeDate.add("mo", 1));
10507     },
10508
10509     // private
10510     showPrevYear : function(){
10511         this.update(this.activeDate.add("y", -1));
10512     },
10513
10514     // private
10515     showNextYear : function(){
10516         this.update(this.activeDate.add("y", 1));
10517     },
10518
10519     // private
10520     handleMouseWheel : function(e){
10521         var delta = e.getWheelDelta();
10522         if(delta > 0){
10523             this.showPrevMonth();
10524             e.stopEvent();
10525         } else if(delta < 0){
10526             this.showNextMonth();
10527             e.stopEvent();
10528         }
10529     },
10530
10531     // private
10532     handleDateClick : function(e, t){
10533         e.stopEvent();
10534         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10535             this.setValue(new Date(t.dateValue));
10536             this.fireEvent("select", this, this.value);
10537         }
10538     },
10539
10540     // private
10541     selectToday : function(){
10542         this.setValue(new Date().clearTime());
10543         this.fireEvent("select", this, this.value);
10544     },
10545
10546     // private
10547     update : function(date){
10548         var vd = this.activeDate;
10549         this.activeDate = date;
10550         if(vd && this.el){
10551             var t = date.getTime();
10552             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10553                 this.cells.removeClass("x-date-selected");
10554                 this.cells.each(function(c){
10555                    if(c.dom.firstChild.dateValue == t){
10556                        c.addClass("x-date-selected");
10557                        setTimeout(function(){
10558                             try{c.dom.firstChild.focus();}catch(e){}
10559                        }, 50);
10560                        return false;
10561                    }
10562                 });
10563                 return;
10564             }
10565         }
10566         var days = date.getDaysInMonth();
10567         var firstOfMonth = date.getFirstDateOfMonth();
10568         var startingPos = firstOfMonth.getDay()-this.startDay;
10569
10570         if(startingPos <= this.startDay){
10571             startingPos += 7;
10572         }
10573
10574         var pm = date.add("mo", -1);
10575         var prevStart = pm.getDaysInMonth()-startingPos;
10576
10577         var cells = this.cells.elements;
10578         var textEls = this.textNodes;
10579         days += startingPos;
10580
10581         // convert everything to numbers so it's fast
10582         var day = 86400000;
10583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10584         var today = new Date().clearTime().getTime();
10585         var sel = date.clearTime().getTime();
10586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10588         var ddMatch = this.disabledDatesRE;
10589         var ddText = this.disabledDatesText;
10590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10591         var ddaysText = this.disabledDaysText;
10592         var format = this.format;
10593
10594         var setCellClass = function(cal, cell){
10595             cell.title = "";
10596             var t = d.getTime();
10597             cell.firstChild.dateValue = t;
10598             if(t == today){
10599                 cell.className += " x-date-today";
10600                 cell.title = cal.todayText;
10601             }
10602             if(t == sel){
10603                 cell.className += " x-date-selected";
10604                 setTimeout(function(){
10605                     try{cell.firstChild.focus();}catch(e){}
10606                 }, 50);
10607             }
10608             // disabling
10609             if(t < min) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.minText;
10612                 return;
10613             }
10614             if(t > max) {
10615                 cell.className = " x-date-disabled";
10616                 cell.title = cal.maxText;
10617                 return;
10618             }
10619             if(ddays){
10620                 if(ddays.indexOf(d.getDay()) != -1){
10621                     cell.title = ddaysText;
10622                     cell.className = " x-date-disabled";
10623                 }
10624             }
10625             if(ddMatch && format){
10626                 var fvalue = d.dateFormat(format);
10627                 if(ddMatch.test(fvalue)){
10628                     cell.title = ddText.replace("%0", fvalue);
10629                     cell.className = " x-date-disabled";
10630                 }
10631             }
10632         };
10633
10634         var i = 0;
10635         for(; i < startingPos; i++) {
10636             textEls[i].innerHTML = (++prevStart);
10637             d.setDate(d.getDate()+1);
10638             cells[i].className = "x-date-prevday";
10639             setCellClass(this, cells[i]);
10640         }
10641         for(; i < days; i++){
10642             intDay = i - startingPos + 1;
10643             textEls[i].innerHTML = (intDay);
10644             d.setDate(d.getDate()+1);
10645             cells[i].className = "x-date-active";
10646             setCellClass(this, cells[i]);
10647         }
10648         var extraDays = 0;
10649         for(; i < 42; i++) {
10650              textEls[i].innerHTML = (++extraDays);
10651              d.setDate(d.getDate()+1);
10652              cells[i].className = "x-date-nextday";
10653              setCellClass(this, cells[i]);
10654         }
10655
10656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10657
10658         if(!this.internalRender){
10659             var main = this.el.dom.firstChild;
10660             var w = main.offsetWidth;
10661             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10662             Roo.fly(main).setWidth(w);
10663             this.internalRender = true;
10664             // opera does not respect the auto grow header center column
10665             // then, after it gets a width opera refuses to recalculate
10666             // without a second pass
10667             if(Roo.isOpera && !this.secondPass){
10668                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10669                 this.secondPass = true;
10670                 this.update.defer(10, this, [date]);
10671             }
10672         }
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684 /**
10685  * @class Roo.TabPanel
10686  * @extends Roo.util.Observable
10687  * A lightweight tab container.
10688  * <br><br>
10689  * Usage:
10690  * <pre><code>
10691 // basic tabs 1, built from existing content
10692 var tabs = new Roo.TabPanel("tabs1");
10693 tabs.addTab("script", "View Script");
10694 tabs.addTab("markup", "View Markup");
10695 tabs.activate("script");
10696
10697 // more advanced tabs, built from javascript
10698 var jtabs = new Roo.TabPanel("jtabs");
10699 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10700
10701 // set up the UpdateManager
10702 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10703 var updater = tab2.getUpdateManager();
10704 updater.setDefaultUrl("ajax1.htm");
10705 tab2.on('activate', updater.refresh, updater, true);
10706
10707 // Use setUrl for Ajax loading
10708 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10709 tab3.setUrl("ajax2.htm", null, true);
10710
10711 // Disabled tab
10712 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10713 tab4.disable();
10714
10715 jtabs.activate("jtabs-1");
10716  * </code></pre>
10717  * @constructor
10718  * Create a new TabPanel.
10719  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10720  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10721  */
10722 Roo.TabPanel = function(container, config){
10723     /**
10724     * The container element for this TabPanel.
10725     * @type Roo.Element
10726     */
10727     this.el = Roo.get(container, true);
10728     if(config){
10729         if(typeof config == "boolean"){
10730             this.tabPosition = config ? "bottom" : "top";
10731         }else{
10732             Roo.apply(this, config);
10733         }
10734     }
10735     if(this.tabPosition == "bottom"){
10736         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10737         this.el.addClass("x-tabs-bottom");
10738     }
10739     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10740     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10741     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10742     if(Roo.isIE){
10743         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10744     }
10745     if(this.tabPosition != "bottom"){
10746     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10747      * @type Roo.Element
10748      */
10749       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750       this.el.addClass("x-tabs-top");
10751     }
10752     this.items = [];
10753
10754     this.bodyEl.setStyle("position", "relative");
10755
10756     this.active = null;
10757     this.activateDelegate = this.activate.createDelegate(this);
10758
10759     this.addEvents({
10760         /**
10761          * @event tabchange
10762          * Fires when the active tab changes
10763          * @param {Roo.TabPanel} this
10764          * @param {Roo.TabPanelItem} activePanel The new active tab
10765          */
10766         "tabchange": true,
10767         /**
10768          * @event beforetabchange
10769          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10770          * @param {Roo.TabPanel} this
10771          * @param {Object} e Set cancel to true on this object to cancel the tab change
10772          * @param {Roo.TabPanelItem} tab The tab being changed to
10773          */
10774         "beforetabchange" : true
10775     });
10776
10777     Roo.EventManager.onWindowResize(this.onResize, this);
10778     this.cpad = this.el.getPadding("lr");
10779     this.hiddenCount = 0;
10780
10781     Roo.TabPanel.superclass.constructor.call(this);
10782 };
10783
10784 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10785         /*
10786          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10787          */
10788     tabPosition : "top",
10789         /*
10790          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10791          */
10792     currentTabWidth : 0,
10793         /*
10794          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     minTabWidth : 40,
10797         /*
10798          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     maxTabWidth : 250,
10801         /*
10802          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10803          */
10804     preferredTabWidth : 175,
10805         /*
10806          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10807          */
10808     resizeTabs : false,
10809         /*
10810          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10811          */
10812     monitorResize : true,
10813
10814     /**
10815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10816      * @param {String} id The id of the div to use <b>or create</b>
10817      * @param {String} text The text for the tab
10818      * @param {String} content (optional) Content to put in the TabPanelItem body
10819      * @param {Boolean} closable (optional) True to create a close icon on the tab
10820      * @return {Roo.TabPanelItem} The created TabPanelItem
10821      */
10822     addTab : function(id, text, content, closable){
10823         var item = new Roo.TabPanelItem(this, id, text, closable);
10824         this.addTabItem(item);
10825         if(content){
10826             item.setContent(content);
10827         }
10828         return item;
10829     },
10830
10831     /**
10832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10834      * @return {Roo.TabPanelItem}
10835      */
10836     getTab : function(id){
10837         return this.items[id];
10838     },
10839
10840     /**
10841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10843      */
10844     hideTab : function(id){
10845         var t = this.items[id];
10846         if(!t.isHidden()){
10847            t.setHidden(true);
10848            this.hiddenCount++;
10849            this.autoSizeTabs();
10850         }
10851     },
10852
10853     /**
10854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10856      */
10857     unhideTab : function(id){
10858         var t = this.items[id];
10859         if(t.isHidden()){
10860            t.setHidden(false);
10861            this.hiddenCount--;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * Adds an existing {@link Roo.TabPanelItem}.
10868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10869      */
10870     addTabItem : function(item){
10871         this.items[item.id] = item;
10872         this.items.push(item);
10873         if(this.resizeTabs){
10874            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10875            this.autoSizeTabs();
10876         }else{
10877             item.autoSize();
10878         }
10879     },
10880
10881     /**
10882      * Removes a {@link Roo.TabPanelItem}.
10883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10884      */
10885     removeTab : function(id){
10886         var items = this.items;
10887         var tab = items[id];
10888         if(!tab) { return; }
10889         var index = items.indexOf(tab);
10890         if(this.active == tab && items.length > 1){
10891             var newTab = this.getNextAvailable(index);
10892             if(newTab) {
10893                 newTab.activate();
10894             }
10895         }
10896         this.stripEl.dom.removeChild(tab.pnode.dom);
10897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10899         }
10900         items.splice(index, 1);
10901         delete this.items[tab.id];
10902         tab.fireEvent("close", tab);
10903         tab.purgeListeners();
10904         this.autoSizeTabs();
10905     },
10906
10907     getNextAvailable : function(start){
10908         var items = this.items;
10909         var index = start;
10910         // look for a next tab that will slide over to
10911         // replace the one being removed
10912         while(index < items.length){
10913             var item = items[++index];
10914             if(item && !item.isHidden()){
10915                 return item;
10916             }
10917         }
10918         // if one isn't found select the previous tab (on the left)
10919         index = start;
10920         while(index >= 0){
10921             var item = items[--index];
10922             if(item && !item.isHidden()){
10923                 return item;
10924             }
10925         }
10926         return null;
10927     },
10928
10929     /**
10930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10932      */
10933     disableTab : function(id){
10934         var tab = this.items[id];
10935         if(tab && this.active != tab){
10936             tab.disable();
10937         }
10938     },
10939
10940     /**
10941      * Enables a {@link Roo.TabPanelItem} that is disabled.
10942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10943      */
10944     enableTab : function(id){
10945         var tab = this.items[id];
10946         tab.enable();
10947     },
10948
10949     /**
10950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10952      * @return {Roo.TabPanelItem} The TabPanelItem.
10953      */
10954     activate : function(id){
10955         var tab = this.items[id];
10956         if(!tab){
10957             return null;
10958         }
10959         if(tab == this.active || tab.disabled){
10960             return tab;
10961         }
10962         var e = {};
10963         this.fireEvent("beforetabchange", this, e, tab);
10964         if(e.cancel !== true && !tab.disabled){
10965             if(this.active){
10966                 this.active.hide();
10967             }
10968             this.active = this.items[id];
10969             this.active.show();
10970             this.fireEvent("tabchange", this, this.active);
10971         }
10972         return tab;
10973     },
10974
10975     /**
10976      * Gets the active {@link Roo.TabPanelItem}.
10977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10978      */
10979     getActiveTab : function(){
10980         return this.active;
10981     },
10982
10983     /**
10984      * Updates the tab body element to fit the height of the container element
10985      * for overflow scrolling
10986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10987      */
10988     syncHeight : function(targetHeight){
10989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10990         var bm = this.bodyEl.getMargins();
10991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10992         this.bodyEl.setHeight(newHeight);
10993         return newHeight;
10994     },
10995
10996     onResize : function(){
10997         if(this.monitorResize){
10998             this.autoSizeTabs();
10999         }
11000     },
11001
11002     /**
11003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     beginUpdate : function(){
11006         this.updating = true;
11007     },
11008
11009     /**
11010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11011      */
11012     endUpdate : function(){
11013         this.updating = false;
11014         this.autoSizeTabs();
11015     },
11016
11017     /**
11018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11019      */
11020     autoSizeTabs : function(){
11021         var count = this.items.length;
11022         var vcount = count - this.hiddenCount;
11023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11024         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11025         var availWidth = Math.floor(w / vcount);
11026         var b = this.stripBody;
11027         if(b.getWidth() > w){
11028             var tabs = this.items;
11029             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11030             if(availWidth < this.minTabWidth){
11031                 /*if(!this.sleft){    // incomplete scrolling code
11032                     this.createScrollButtons();
11033                 }
11034                 this.showScroll();
11035                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11036             }
11037         }else{
11038             if(this.currentTabWidth < this.preferredTabWidth){
11039                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11040             }
11041         }
11042     },
11043
11044     /**
11045      * Returns the number of tabs in this TabPanel.
11046      * @return {Number}
11047      */
11048      getCount : function(){
11049          return this.items.length;
11050      },
11051
11052     /**
11053      * Resizes all the tabs to the passed width
11054      * @param {Number} The new width
11055      */
11056     setTabWidth : function(width){
11057         this.currentTabWidth = width;
11058         for(var i = 0, len = this.items.length; i < len; i++) {
11059                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11060         }
11061     },
11062
11063     /**
11064      * Destroys this TabPanel
11065      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11066      */
11067     destroy : function(removeEl){
11068         Roo.EventManager.removeResizeListener(this.onResize, this);
11069         for(var i = 0, len = this.items.length; i < len; i++){
11070             this.items[i].purgeListeners();
11071         }
11072         if(removeEl === true){
11073             this.el.update("");
11074             this.el.remove();
11075         }
11076     }
11077 });
11078
11079 /**
11080  * @class Roo.TabPanelItem
11081  * @extends Roo.util.Observable
11082  * Represents an individual item (tab plus body) in a TabPanel.
11083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11084  * @param {String} id The id of this TabPanelItem
11085  * @param {String} text The text for the tab of this TabPanelItem
11086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11087  */
11088 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11089     /**
11090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11091      * @type Roo.TabPanel
11092      */
11093     this.tabPanel = tabPanel;
11094     /**
11095      * The id for this TabPanelItem
11096      * @type String
11097      */
11098     this.id = id;
11099     /** @private */
11100     this.disabled = false;
11101     /** @private */
11102     this.text = text;
11103     /** @private */
11104     this.loaded = false;
11105     this.closable = closable;
11106
11107     /**
11108      * The body element for this TabPanelItem.
11109      * @type Roo.Element
11110      */
11111     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11113     this.bodyEl.setStyle("display", "block");
11114     this.bodyEl.setStyle("zoom", "1");
11115     this.hideAction();
11116
11117     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11118     /** @private */
11119     this.el = Roo.get(els.el, true);
11120     this.inner = Roo.get(els.inner, true);
11121     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11122     this.pnode = Roo.get(els.el.parentNode, true);
11123     this.el.on("mousedown", this.onTabMouseDown, this);
11124     this.el.on("click", this.onTabClick, this);
11125     /** @private */
11126     if(closable){
11127         var c = Roo.get(els.close, true);
11128         c.dom.title = this.closeText;
11129         c.addClassOnOver("close-over");
11130         c.on("click", this.closeClick, this);
11131      }
11132
11133     this.addEvents({
11134          /**
11135          * @event activate
11136          * Fires when this tab becomes the active tab.
11137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11138          * @param {Roo.TabPanelItem} this
11139          */
11140         "activate": true,
11141         /**
11142          * @event beforeclose
11143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11144          * @param {Roo.TabPanelItem} this
11145          * @param {Object} e Set cancel to true on this object to cancel the close.
11146          */
11147         "beforeclose": true,
11148         /**
11149          * @event close
11150          * Fires when this tab is closed.
11151          * @param {Roo.TabPanelItem} this
11152          */
11153          "close": true,
11154         /**
11155          * @event deactivate
11156          * Fires when this tab is no longer the active tab.
11157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11158          * @param {Roo.TabPanelItem} this
11159          */
11160          "deactivate" : true
11161     });
11162     this.hidden = false;
11163
11164     Roo.TabPanelItem.superclass.constructor.call(this);
11165 };
11166
11167 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11168     purgeListeners : function(){
11169        Roo.util.Observable.prototype.purgeListeners.call(this);
11170        this.el.removeAllListeners();
11171     },
11172     /**
11173      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11174      */
11175     show : function(){
11176         this.pnode.addClass("on");
11177         this.showAction();
11178         if(Roo.isOpera){
11179             this.tabPanel.stripWrap.repaint();
11180         }
11181         this.fireEvent("activate", this.tabPanel, this);
11182     },
11183
11184     /**
11185      * Returns true if this tab is the active tab.
11186      * @return {Boolean}
11187      */
11188     isActive : function(){
11189         return this.tabPanel.getActiveTab() == this;
11190     },
11191
11192     /**
11193      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11194      */
11195     hide : function(){
11196         this.pnode.removeClass("on");
11197         this.hideAction();
11198         this.fireEvent("deactivate", this.tabPanel, this);
11199     },
11200
11201     hideAction : function(){
11202         this.bodyEl.hide();
11203         this.bodyEl.setStyle("position", "absolute");
11204         this.bodyEl.setLeft("-20000px");
11205         this.bodyEl.setTop("-20000px");
11206     },
11207
11208     showAction : function(){
11209         this.bodyEl.setStyle("position", "relative");
11210         this.bodyEl.setTop("");
11211         this.bodyEl.setLeft("");
11212         this.bodyEl.show();
11213     },
11214
11215     /**
11216      * Set the tooltip for the tab.
11217      * @param {String} tooltip The tab's tooltip
11218      */
11219     setTooltip : function(text){
11220         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11221             this.textEl.dom.qtip = text;
11222             this.textEl.dom.removeAttribute('title');
11223         }else{
11224             this.textEl.dom.title = text;
11225         }
11226     },
11227
11228     onTabClick : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     onTabMouseDown : function(e){
11234         e.preventDefault();
11235         this.tabPanel.activate(this.id);
11236     },
11237
11238     getWidth : function(){
11239         return this.inner.getWidth();
11240     },
11241
11242     setWidth : function(width){
11243         var iwidth = width - this.pnode.getPadding("lr");
11244         this.inner.setWidth(iwidth);
11245         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11246         this.pnode.setWidth(width);
11247     },
11248
11249     /**
11250      * Show or hide the tab
11251      * @param {Boolean} hidden True to hide or false to show.
11252      */
11253     setHidden : function(hidden){
11254         this.hidden = hidden;
11255         this.pnode.setStyle("display", hidden ? "none" : "");
11256     },
11257
11258     /**
11259      * Returns true if this tab is "hidden"
11260      * @return {Boolean}
11261      */
11262     isHidden : function(){
11263         return this.hidden;
11264     },
11265
11266     /**
11267      * Returns the text for this tab
11268      * @return {String}
11269      */
11270     getText : function(){
11271         return this.text;
11272     },
11273
11274     autoSize : function(){
11275         //this.el.beginMeasure();
11276         this.textEl.setWidth(1);
11277         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11278         //this.el.endMeasure();
11279     },
11280
11281     /**
11282      * Sets the text for the tab (Note: this also sets the tooltip text)
11283      * @param {String} text The tab's text and tooltip
11284      */
11285     setText : function(text){
11286         this.text = text;
11287         this.textEl.update(text);
11288         this.setTooltip(text);
11289         if(!this.tabPanel.resizeTabs){
11290             this.autoSize();
11291         }
11292     },
11293     /**
11294      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11295      */
11296     activate : function(){
11297         this.tabPanel.activate(this.id);
11298     },
11299
11300     /**
11301      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11302      */
11303     disable : function(){
11304         if(this.tabPanel.active != this){
11305             this.disabled = true;
11306             this.pnode.addClass("disabled");
11307         }
11308     },
11309
11310     /**
11311      * Enables this TabPanelItem if it was previously disabled.
11312      */
11313     enable : function(){
11314         this.disabled = false;
11315         this.pnode.removeClass("disabled");
11316     },
11317
11318     /**
11319      * Sets the content for this TabPanelItem.
11320      * @param {String} content The content
11321      * @param {Boolean} loadScripts true to look for and load scripts
11322      */
11323     setContent : function(content, loadScripts){
11324         this.bodyEl.update(content, loadScripts);
11325     },
11326
11327     /**
11328      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11329      * @return {Roo.UpdateManager} The UpdateManager
11330      */
11331     getUpdateManager : function(){
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /**
11336      * Set a URL to be used to load the content for this TabPanelItem.
11337      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11338      * @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)
11339      * @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)
11340      * @return {Roo.UpdateManager} The UpdateManager
11341      */
11342     setUrl : function(url, params, loadOnce){
11343         if(this.refreshDelegate){
11344             this.un('activate', this.refreshDelegate);
11345         }
11346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11347         this.on("activate", this.refreshDelegate);
11348         return this.bodyEl.getUpdateManager();
11349     },
11350
11351     /** @private */
11352     _handleRefresh : function(url, params, loadOnce){
11353         if(!loadOnce || !this.loaded){
11354             var updater = this.bodyEl.getUpdateManager();
11355             updater.update(url, params, this._setLoaded.createDelegate(this));
11356         }
11357     },
11358
11359     /**
11360      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11361      *   Will fail silently if the setUrl method has not been called.
11362      *   This does not activate the panel, just updates its content.
11363      */
11364     refresh : function(){
11365         if(this.refreshDelegate){
11366            this.loaded = false;
11367            this.refreshDelegate();
11368         }
11369     },
11370
11371     /** @private */
11372     _setLoaded : function(){
11373         this.loaded = true;
11374     },
11375
11376     /** @private */
11377     closeClick : function(e){
11378         var o = {};
11379         e.stopEvent();
11380         this.fireEvent("beforeclose", this, o);
11381         if(o.cancel !== true){
11382             this.tabPanel.removeTab(this.id);
11383         }
11384     },
11385     /**
11386      * The text displayed in the tooltip for the close icon.
11387      * @type String
11388      */
11389     closeText : "Close this tab"
11390 });
11391
11392 /** @private */
11393 Roo.TabPanel.prototype.createStrip = function(container){
11394     var strip = document.createElement("div");
11395     strip.className = "x-tabs-wrap";
11396     container.appendChild(strip);
11397     return strip;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createStripList = function(strip){
11401     // div wrapper for retard IE
11402     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11403     return strip.firstChild.firstChild.firstChild.firstChild;
11404 };
11405 /** @private */
11406 Roo.TabPanel.prototype.createBody = function(container){
11407     var body = document.createElement("div");
11408     Roo.id(body, "tab-body");
11409     Roo.fly(body).addClass("x-tabs-body");
11410     container.appendChild(body);
11411     return body;
11412 };
11413 /** @private */
11414 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11415     var body = Roo.getDom(id);
11416     if(!body){
11417         body = document.createElement("div");
11418         body.id = id;
11419     }
11420     Roo.fly(body).addClass("x-tabs-item-body");
11421     bodyEl.insertBefore(body, bodyEl.firstChild);
11422     return body;
11423 };
11424 /** @private */
11425 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11426     var td = document.createElement("td");
11427     stripEl.appendChild(td);
11428     if(closable){
11429         td.className = "x-tabs-closable";
11430         if(!this.closeTpl){
11431             this.closeTpl = new Roo.Template(
11432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11435             );
11436         }
11437         var el = this.closeTpl.overwrite(td, {"text": text});
11438         var close = el.getElementsByTagName("div")[0];
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "close": close, "inner": inner};
11441     } else {
11442         if(!this.tabTpl){
11443             this.tabTpl = new Roo.Template(
11444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11446             );
11447         }
11448         var el = this.tabTpl.overwrite(td, {"text": text});
11449         var inner = el.getElementsByTagName("em")[0];
11450         return {"el": el, "inner": inner};
11451     }
11452 };/*
11453  * Based on:
11454  * Ext JS Library 1.1.1
11455  * Copyright(c) 2006-2007, Ext JS, LLC.
11456  *
11457  * Originally Released Under LGPL - original licence link has changed is not relivant.
11458  *
11459  * Fork - LGPL
11460  * <script type="text/javascript">
11461  */
11462
11463 /**
11464  * @class Roo.Button
11465  * @extends Roo.util.Observable
11466  * Simple Button class
11467  * @cfg {String} text The button text
11468  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11469  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11470  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11471  * @cfg {Object} scope The scope of the handler
11472  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11473  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11474  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11475  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11476  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11477  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11478    applies if enableToggle = true)
11479  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11480  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11481   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11482  * @constructor
11483  * Create a new button
11484  * @param {Object} config The config object
11485  */
11486 Roo.Button = function(renderTo, config)
11487 {
11488     if (!config) {
11489         config = renderTo;
11490         renderTo = config.renderTo || false;
11491     }
11492     
11493     Roo.apply(this, config);
11494     this.addEvents({
11495         /**
11496              * @event click
11497              * Fires when this button is clicked
11498              * @param {Button} this
11499              * @param {EventObject} e The click event
11500              */
11501             "click" : true,
11502         /**
11503              * @event toggle
11504              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11505              * @param {Button} this
11506              * @param {Boolean} pressed
11507              */
11508             "toggle" : true,
11509         /**
11510              * @event mouseover
11511              * Fires when the mouse hovers over the button
11512              * @param {Button} this
11513              * @param {Event} e The event object
11514              */
11515         'mouseover' : true,
11516         /**
11517              * @event mouseout
11518              * Fires when the mouse exits the button
11519              * @param {Button} this
11520              * @param {Event} e The event object
11521              */
11522         'mouseout': true,
11523          /**
11524              * @event render
11525              * Fires when the button is rendered
11526              * @param {Button} this
11527              */
11528         'render': true
11529     });
11530     if(this.menu){
11531         this.menu = Roo.menu.MenuMgr.get(this.menu);
11532     }
11533     // register listeners first!!  - so render can be captured..
11534     Roo.util.Observable.call(this);
11535     if(renderTo){
11536         this.render(renderTo);
11537     }
11538     
11539   
11540 };
11541
11542 Roo.extend(Roo.Button, Roo.util.Observable, {
11543     /**
11544      * 
11545      */
11546     
11547     /**
11548      * Read-only. True if this button is hidden
11549      * @type Boolean
11550      */
11551     hidden : false,
11552     /**
11553      * Read-only. True if this button is disabled
11554      * @type Boolean
11555      */
11556     disabled : false,
11557     /**
11558      * Read-only. True if this button is pressed (only if enableToggle = true)
11559      * @type Boolean
11560      */
11561     pressed : false,
11562
11563     /**
11564      * @cfg {Number} tabIndex 
11565      * The DOM tabIndex for this button (defaults to undefined)
11566      */
11567     tabIndex : undefined,
11568
11569     /**
11570      * @cfg {Boolean} enableToggle
11571      * True to enable pressed/not pressed toggling (defaults to false)
11572      */
11573     enableToggle: false,
11574     /**
11575      * @cfg {Mixed} menu
11576      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11577      */
11578     menu : undefined,
11579     /**
11580      * @cfg {String} menuAlign
11581      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11582      */
11583     menuAlign : "tl-bl?",
11584
11585     /**
11586      * @cfg {String} iconCls
11587      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11588      */
11589     iconCls : undefined,
11590     /**
11591      * @cfg {String} type
11592      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11593      */
11594     type : 'button',
11595
11596     // private
11597     menuClassTarget: 'tr',
11598
11599     /**
11600      * @cfg {String} clickEvent
11601      * The type of event to map to the button's event handler (defaults to 'click')
11602      */
11603     clickEvent : 'click',
11604
11605     /**
11606      * @cfg {Boolean} handleMouseEvents
11607      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11608      */
11609     handleMouseEvents : true,
11610
11611     /**
11612      * @cfg {String} tooltipType
11613      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11614      */
11615     tooltipType : 'qtip',
11616
11617     /**
11618      * @cfg {String} cls
11619      * A CSS class to apply to the button's main element.
11620      */
11621     
11622     /**
11623      * @cfg {Roo.Template} template (Optional)
11624      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11625      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11626      * require code modifications if required elements (e.g. a button) aren't present.
11627      */
11628
11629     // private
11630     render : function(renderTo){
11631         var btn;
11632         if(this.hideParent){
11633             this.parentEl = Roo.get(renderTo);
11634         }
11635         if(!this.dhconfig){
11636             if(!this.template){
11637                 if(!Roo.Button.buttonTemplate){
11638                     // hideous table template
11639                     Roo.Button.buttonTemplate = new Roo.Template(
11640                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11641                         '<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>',
11642                         "</tr></tbody></table>");
11643                 }
11644                 this.template = Roo.Button.buttonTemplate;
11645             }
11646             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11647             var btnEl = btn.child("button:first");
11648             btnEl.on('focus', this.onFocus, this);
11649             btnEl.on('blur', this.onBlur, this);
11650             if(this.cls){
11651                 btn.addClass(this.cls);
11652             }
11653             if(this.icon){
11654                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11655             }
11656             if(this.iconCls){
11657                 btnEl.addClass(this.iconCls);
11658                 if(!this.cls){
11659                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11660                 }
11661             }
11662             if(this.tabIndex !== undefined){
11663                 btnEl.dom.tabIndex = this.tabIndex;
11664             }
11665             if(this.tooltip){
11666                 if(typeof this.tooltip == 'object'){
11667                     Roo.QuickTips.tips(Roo.apply({
11668                           target: btnEl.id
11669                     }, this.tooltip));
11670                 } else {
11671                     btnEl.dom[this.tooltipType] = this.tooltip;
11672                 }
11673             }
11674         }else{
11675             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11676         }
11677         this.el = btn;
11678         if(this.id){
11679             this.el.dom.id = this.el.id = this.id;
11680         }
11681         if(this.menu){
11682             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11683             this.menu.on("show", this.onMenuShow, this);
11684             this.menu.on("hide", this.onMenuHide, this);
11685         }
11686         btn.addClass("x-btn");
11687         if(Roo.isIE && !Roo.isIE7){
11688             this.autoWidth.defer(1, this);
11689         }else{
11690             this.autoWidth();
11691         }
11692         if(this.handleMouseEvents){
11693             btn.on("mouseover", this.onMouseOver, this);
11694             btn.on("mouseout", this.onMouseOut, this);
11695             btn.on("mousedown", this.onMouseDown, this);
11696         }
11697         btn.on(this.clickEvent, this.onClick, this);
11698         //btn.on("mouseup", this.onMouseUp, this);
11699         if(this.hidden){
11700             this.hide();
11701         }
11702         if(this.disabled){
11703             this.disable();
11704         }
11705         Roo.ButtonToggleMgr.register(this);
11706         if(this.pressed){
11707             this.el.addClass("x-btn-pressed");
11708         }
11709         if(this.repeat){
11710             var repeater = new Roo.util.ClickRepeater(btn,
11711                 typeof this.repeat == "object" ? this.repeat : {}
11712             );
11713             repeater.on("click", this.onClick,  this);
11714         }
11715         
11716         this.fireEvent('render', this);
11717         
11718     },
11719     /**
11720      * Returns the button's underlying element
11721      * @return {Roo.Element} The element
11722      */
11723     getEl : function(){
11724         return this.el;  
11725     },
11726     
11727     /**
11728      * Destroys this Button and removes any listeners.
11729      */
11730     destroy : function(){
11731         Roo.ButtonToggleMgr.unregister(this);
11732         this.el.removeAllListeners();
11733         this.purgeListeners();
11734         this.el.remove();
11735     },
11736
11737     // private
11738     autoWidth : function(){
11739         if(this.el){
11740             this.el.setWidth("auto");
11741             if(Roo.isIE7 && Roo.isStrict){
11742                 var ib = this.el.child('button');
11743                 if(ib && ib.getWidth() > 20){
11744                     ib.clip();
11745                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11746                 }
11747             }
11748             if(this.minWidth){
11749                 if(this.hidden){
11750                     this.el.beginMeasure();
11751                 }
11752                 if(this.el.getWidth() < this.minWidth){
11753                     this.el.setWidth(this.minWidth);
11754                 }
11755                 if(this.hidden){
11756                     this.el.endMeasure();
11757                 }
11758             }
11759         }
11760     },
11761
11762     /**
11763      * Assigns this button's click handler
11764      * @param {Function} handler The function to call when the button is clicked
11765      * @param {Object} scope (optional) Scope for the function passed in
11766      */
11767     setHandler : function(handler, scope){
11768         this.handler = handler;
11769         this.scope = scope;  
11770     },
11771     
11772     /**
11773      * Sets this button's text
11774      * @param {String} text The button text
11775      */
11776     setText : function(text){
11777         this.text = text;
11778         if(this.el){
11779             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11780         }
11781         this.autoWidth();
11782     },
11783     
11784     /**
11785      * Gets the text for this button
11786      * @return {String} The button text
11787      */
11788     getText : function(){
11789         return this.text;  
11790     },
11791     
11792     /**
11793      * Show this button
11794      */
11795     show: function(){
11796         this.hidden = false;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11799         }
11800     },
11801     
11802     /**
11803      * Hide this button
11804      */
11805     hide: function(){
11806         this.hidden = true;
11807         if(this.el){
11808             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11809         }
11810     },
11811     
11812     /**
11813      * Convenience function for boolean show/hide
11814      * @param {Boolean} visible True to show, false to hide
11815      */
11816     setVisible: function(visible){
11817         if(visible) {
11818             this.show();
11819         }else{
11820             this.hide();
11821         }
11822     },
11823     
11824     /**
11825      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11826      * @param {Boolean} state (optional) Force a particular state
11827      */
11828     toggle : function(state){
11829         state = state === undefined ? !this.pressed : state;
11830         if(state != this.pressed){
11831             if(state){
11832                 this.el.addClass("x-btn-pressed");
11833                 this.pressed = true;
11834                 this.fireEvent("toggle", this, true);
11835             }else{
11836                 this.el.removeClass("x-btn-pressed");
11837                 this.pressed = false;
11838                 this.fireEvent("toggle", this, false);
11839             }
11840             if(this.toggleHandler){
11841                 this.toggleHandler.call(this.scope || this, this, state);
11842             }
11843         }
11844     },
11845     
11846     /**
11847      * Focus the button
11848      */
11849     focus : function(){
11850         this.el.child('button:first').focus();
11851     },
11852     
11853     /**
11854      * Disable this button
11855      */
11856     disable : function(){
11857         if(this.el){
11858             this.el.addClass("x-btn-disabled");
11859         }
11860         this.disabled = true;
11861     },
11862     
11863     /**
11864      * Enable this button
11865      */
11866     enable : function(){
11867         if(this.el){
11868             this.el.removeClass("x-btn-disabled");
11869         }
11870         this.disabled = false;
11871     },
11872
11873     /**
11874      * Convenience function for boolean enable/disable
11875      * @param {Boolean} enabled True to enable, false to disable
11876      */
11877     setDisabled : function(v){
11878         this[v !== true ? "enable" : "disable"]();
11879     },
11880
11881     // private
11882     onClick : function(e){
11883         if(e){
11884             e.preventDefault();
11885         }
11886         if(e.button != 0){
11887             return;
11888         }
11889         if(!this.disabled){
11890             if(this.enableToggle){
11891                 this.toggle();
11892             }
11893             if(this.menu && !this.menu.isVisible()){
11894                 this.menu.show(this.el, this.menuAlign);
11895             }
11896             this.fireEvent("click", this, e);
11897             if(this.handler){
11898                 this.el.removeClass("x-btn-over");
11899                 this.handler.call(this.scope || this, this, e);
11900             }
11901         }
11902     },
11903     // private
11904     onMouseOver : function(e){
11905         if(!this.disabled){
11906             this.el.addClass("x-btn-over");
11907             this.fireEvent('mouseover', this, e);
11908         }
11909     },
11910     // private
11911     onMouseOut : function(e){
11912         if(!e.within(this.el,  true)){
11913             this.el.removeClass("x-btn-over");
11914             this.fireEvent('mouseout', this, e);
11915         }
11916     },
11917     // private
11918     onFocus : function(e){
11919         if(!this.disabled){
11920             this.el.addClass("x-btn-focus");
11921         }
11922     },
11923     // private
11924     onBlur : function(e){
11925         this.el.removeClass("x-btn-focus");
11926     },
11927     // private
11928     onMouseDown : function(e){
11929         if(!this.disabled && e.button == 0){
11930             this.el.addClass("x-btn-click");
11931             Roo.get(document).on('mouseup', this.onMouseUp, this);
11932         }
11933     },
11934     // private
11935     onMouseUp : function(e){
11936         if(e.button == 0){
11937             this.el.removeClass("x-btn-click");
11938             Roo.get(document).un('mouseup', this.onMouseUp, this);
11939         }
11940     },
11941     // private
11942     onMenuShow : function(e){
11943         this.el.addClass("x-btn-menu-active");
11944     },
11945     // private
11946     onMenuHide : function(e){
11947         this.el.removeClass("x-btn-menu-active");
11948     }   
11949 });
11950
11951 // Private utility class used by Button
11952 Roo.ButtonToggleMgr = function(){
11953    var groups = {};
11954    
11955    function toggleGroup(btn, state){
11956        if(state){
11957            var g = groups[btn.toggleGroup];
11958            for(var i = 0, l = g.length; i < l; i++){
11959                if(g[i] != btn){
11960                    g[i].toggle(false);
11961                }
11962            }
11963        }
11964    }
11965    
11966    return {
11967        register : function(btn){
11968            if(!btn.toggleGroup){
11969                return;
11970            }
11971            var g = groups[btn.toggleGroup];
11972            if(!g){
11973                g = groups[btn.toggleGroup] = [];
11974            }
11975            g.push(btn);
11976            btn.on("toggle", toggleGroup);
11977        },
11978        
11979        unregister : function(btn){
11980            if(!btn.toggleGroup){
11981                return;
11982            }
11983            var g = groups[btn.toggleGroup];
11984            if(g){
11985                g.remove(btn);
11986                btn.un("toggle", toggleGroup);
11987            }
11988        }
11989    };
11990 }();/*
11991  * Based on:
11992  * Ext JS Library 1.1.1
11993  * Copyright(c) 2006-2007, Ext JS, LLC.
11994  *
11995  * Originally Released Under LGPL - original licence link has changed is not relivant.
11996  *
11997  * Fork - LGPL
11998  * <script type="text/javascript">
11999  */
12000  
12001 /**
12002  * @class Roo.SplitButton
12003  * @extends Roo.Button
12004  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12005  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12006  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12007  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12008  * @cfg {String} arrowTooltip The title attribute of the arrow
12009  * @constructor
12010  * Create a new menu button
12011  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12012  * @param {Object} config The config object
12013  */
12014 Roo.SplitButton = function(renderTo, config){
12015     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12016     /**
12017      * @event arrowclick
12018      * Fires when this button's arrow is clicked
12019      * @param {SplitButton} this
12020      * @param {EventObject} e The click event
12021      */
12022     this.addEvents({"arrowclick":true});
12023 };
12024
12025 Roo.extend(Roo.SplitButton, Roo.Button, {
12026     render : function(renderTo){
12027         // this is one sweet looking template!
12028         var tpl = new Roo.Template(
12029             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12031             '<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>',
12032             "</tbody></table></td><td>",
12033             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12034             '<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>',
12035             "</tbody></table></td></tr></table>"
12036         );
12037         var btn = tpl.append(renderTo, [this.text, this.type], true);
12038         var btnEl = btn.child("button");
12039         if(this.cls){
12040             btn.addClass(this.cls);
12041         }
12042         if(this.icon){
12043             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12044         }
12045         if(this.iconCls){
12046             btnEl.addClass(this.iconCls);
12047             if(!this.cls){
12048                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12049             }
12050         }
12051         this.el = btn;
12052         if(this.handleMouseEvents){
12053             btn.on("mouseover", this.onMouseOver, this);
12054             btn.on("mouseout", this.onMouseOut, this);
12055             btn.on("mousedown", this.onMouseDown, this);
12056             btn.on("mouseup", this.onMouseUp, this);
12057         }
12058         btn.on(this.clickEvent, this.onClick, this);
12059         if(this.tooltip){
12060             if(typeof this.tooltip == 'object'){
12061                 Roo.QuickTips.tips(Roo.apply({
12062                       target: btnEl.id
12063                 }, this.tooltip));
12064             } else {
12065                 btnEl.dom[this.tooltipType] = this.tooltip;
12066             }
12067         }
12068         if(this.arrowTooltip){
12069             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12070         }
12071         if(this.hidden){
12072             this.hide();
12073         }
12074         if(this.disabled){
12075             this.disable();
12076         }
12077         if(this.pressed){
12078             this.el.addClass("x-btn-pressed");
12079         }
12080         if(Roo.isIE && !Roo.isIE7){
12081             this.autoWidth.defer(1, this);
12082         }else{
12083             this.autoWidth();
12084         }
12085         if(this.menu){
12086             this.menu.on("show", this.onMenuShow, this);
12087             this.menu.on("hide", this.onMenuHide, this);
12088         }
12089         this.fireEvent('render', this);
12090     },
12091
12092     // private
12093     autoWidth : function(){
12094         if(this.el){
12095             var tbl = this.el.child("table:first");
12096             var tbl2 = this.el.child("table:last");
12097             this.el.setWidth("auto");
12098             tbl.setWidth("auto");
12099             if(Roo.isIE7 && Roo.isStrict){
12100                 var ib = this.el.child('button:first');
12101                 if(ib && ib.getWidth() > 20){
12102                     ib.clip();
12103                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12104                 }
12105             }
12106             if(this.minWidth){
12107                 if(this.hidden){
12108                     this.el.beginMeasure();
12109                 }
12110                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12111                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12112                 }
12113                 if(this.hidden){
12114                     this.el.endMeasure();
12115                 }
12116             }
12117             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12118         } 
12119     },
12120     /**
12121      * Sets this button's click handler
12122      * @param {Function} handler The function to call when the button is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setHandler : function(handler, scope){
12126         this.handler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Sets this button's arrow click handler
12132      * @param {Function} handler The function to call when the arrow is clicked
12133      * @param {Object} scope (optional) Scope for the function passed above
12134      */
12135     setArrowHandler : function(handler, scope){
12136         this.arrowHandler = handler;
12137         this.scope = scope;  
12138     },
12139     
12140     /**
12141      * Focus the button
12142      */
12143     focus : function(){
12144         if(this.el){
12145             this.el.child("button:first").focus();
12146         }
12147     },
12148
12149     // private
12150     onClick : function(e){
12151         e.preventDefault();
12152         if(!this.disabled){
12153             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12154                 if(this.menu && !this.menu.isVisible()){
12155                     this.menu.show(this.el, this.menuAlign);
12156                 }
12157                 this.fireEvent("arrowclick", this, e);
12158                 if(this.arrowHandler){
12159                     this.arrowHandler.call(this.scope || this, this, e);
12160                 }
12161             }else{
12162                 this.fireEvent("click", this, e);
12163                 if(this.handler){
12164                     this.handler.call(this.scope || this, this, e);
12165                 }
12166             }
12167         }
12168     },
12169     // private
12170     onMouseDown : function(e){
12171         if(!this.disabled){
12172             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12173         }
12174     },
12175     // private
12176     onMouseUp : function(e){
12177         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12178     }   
12179 });
12180
12181
12182 // backwards compat
12183 Roo.MenuButton = Roo.SplitButton;/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193
12194 /**
12195  * @class Roo.Toolbar
12196  * Basic Toolbar class.
12197  * @constructor
12198  * Creates a new Toolbar
12199  * @param {Object} config The config object
12200  */ 
12201 Roo.Toolbar = function(container, buttons, config)
12202 {
12203     /// old consturctor format still supported..
12204     if(container instanceof Array){ // omit the container for later rendering
12205         buttons = container;
12206         config = buttons;
12207         container = null;
12208     }
12209     if (typeof(container) == 'object' && container.xtype) {
12210         config = container;
12211         container = config.container;
12212         buttons = config.buttons; // not really - use items!!
12213     }
12214     var xitems = [];
12215     if (config && config.items) {
12216         xitems = config.items;
12217         delete config.items;
12218     }
12219     Roo.apply(this, config);
12220     this.buttons = buttons;
12221     
12222     if(container){
12223         this.render(container);
12224     }
12225     Roo.each(xitems, function(b) {
12226         this.add(b);
12227     }, this);
12228     
12229 };
12230
12231 Roo.Toolbar.prototype = {
12232     /**
12233      * @cfg {Roo.data.Store} items
12234      * array of button configs or elements to add
12235      */
12236     
12237     /**
12238      * @cfg {String/HTMLElement/Element} container
12239      * The id or element that will contain the toolbar
12240      */
12241     // private
12242     render : function(ct){
12243         this.el = Roo.get(ct);
12244         if(this.cls){
12245             this.el.addClass(this.cls);
12246         }
12247         // using a table allows for vertical alignment
12248         // 100% width is needed by Safari...
12249         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12250         this.tr = this.el.child("tr", true);
12251         var autoId = 0;
12252         this.items = new Roo.util.MixedCollection(false, function(o){
12253             return o.id || ("item" + (++autoId));
12254         });
12255         if(this.buttons){
12256             this.add.apply(this, this.buttons);
12257             delete this.buttons;
12258         }
12259     },
12260
12261     /**
12262      * Adds element(s) to the toolbar -- this function takes a variable number of 
12263      * arguments of mixed type and adds them to the toolbar.
12264      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12265      * <ul>
12266      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12267      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12268      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12269      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12270      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12271      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12272      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12273      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12274      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12275      * </ul>
12276      * @param {Mixed} arg2
12277      * @param {Mixed} etc.
12278      */
12279     add : function(){
12280         var a = arguments, l = a.length;
12281         for(var i = 0; i < l; i++){
12282             this._add(a[i]);
12283         }
12284     },
12285     // private..
12286     _add : function(el) {
12287         
12288         if (el.xtype) {
12289             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12290         }
12291         
12292         if (el.applyTo){ // some kind of form field
12293             return this.addField(el);
12294         } 
12295         if (el.render){ // some kind of Toolbar.Item
12296             return this.addItem(el);
12297         }
12298         if (typeof el == "string"){ // string
12299             if(el == "separator" || el == "-"){
12300                 return this.addSeparator();
12301             }
12302             if (el == " "){
12303                 return this.addSpacer();
12304             }
12305             if(el == "->"){
12306                 return this.addFill();
12307             }
12308             return this.addText(el);
12309             
12310         }
12311         if(el.tagName){ // element
12312             return this.addElement(el);
12313         }
12314         if(typeof el == "object"){ // must be button config?
12315             return this.addButton(el);
12316         }
12317         // and now what?!?!
12318         return false;
12319         
12320     },
12321     
12322     /**
12323      * Add an Xtype element
12324      * @param {Object} xtype Xtype Object
12325      * @return {Object} created Object
12326      */
12327     addxtype : function(e){
12328         return this.add(e);  
12329     },
12330     
12331     /**
12332      * Returns the Element for this toolbar.
12333      * @return {Roo.Element}
12334      */
12335     getEl : function(){
12336         return this.el;  
12337     },
12338     
12339     /**
12340      * Adds a separator
12341      * @return {Roo.Toolbar.Item} The separator item
12342      */
12343     addSeparator : function(){
12344         return this.addItem(new Roo.Toolbar.Separator());
12345     },
12346
12347     /**
12348      * Adds a spacer element
12349      * @return {Roo.Toolbar.Spacer} The spacer item
12350      */
12351     addSpacer : function(){
12352         return this.addItem(new Roo.Toolbar.Spacer());
12353     },
12354
12355     /**
12356      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12357      * @return {Roo.Toolbar.Fill} The fill item
12358      */
12359     addFill : function(){
12360         return this.addItem(new Roo.Toolbar.Fill());
12361     },
12362
12363     /**
12364      * Adds any standard HTML element to the toolbar
12365      * @param {String/HTMLElement/Element} el The element or id of the element to add
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addElement : function(el){
12369         return this.addItem(new Roo.Toolbar.Item(el));
12370     },
12371     /**
12372      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12373      * @type Roo.util.MixedCollection  
12374      */
12375     items : false,
12376      
12377     /**
12378      * Adds any Toolbar.Item or subclass
12379      * @param {Roo.Toolbar.Item} item
12380      * @return {Roo.Toolbar.Item} The item
12381      */
12382     addItem : function(item){
12383         var td = this.nextBlock();
12384         item.render(td);
12385         this.items.add(item);
12386         return item;
12387     },
12388     
12389     /**
12390      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12391      * @param {Object/Array} config A button config or array of configs
12392      * @return {Roo.Toolbar.Button/Array}
12393      */
12394     addButton : function(config){
12395         if(config instanceof Array){
12396             var buttons = [];
12397             for(var i = 0, len = config.length; i < len; i++) {
12398                 buttons.push(this.addButton(config[i]));
12399             }
12400             return buttons;
12401         }
12402         var b = config;
12403         if(!(config instanceof Roo.Toolbar.Button)){
12404             b = config.split ?
12405                 new Roo.Toolbar.SplitButton(config) :
12406                 new Roo.Toolbar.Button(config);
12407         }
12408         var td = this.nextBlock();
12409         b.render(td);
12410         this.items.add(b);
12411         return b;
12412     },
12413     
12414     /**
12415      * Adds text to the toolbar
12416      * @param {String} text The text to add
12417      * @return {Roo.Toolbar.Item} The element's item
12418      */
12419     addText : function(text){
12420         return this.addItem(new Roo.Toolbar.TextItem(text));
12421     },
12422     
12423     /**
12424      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12425      * @param {Number} index The index where the item is to be inserted
12426      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12427      * @return {Roo.Toolbar.Button/Item}
12428      */
12429     insertButton : function(index, item){
12430         if(item instanceof Array){
12431             var buttons = [];
12432             for(var i = 0, len = item.length; i < len; i++) {
12433                buttons.push(this.insertButton(index + i, item[i]));
12434             }
12435             return buttons;
12436         }
12437         if (!(item instanceof Roo.Toolbar.Button)){
12438            item = new Roo.Toolbar.Button(item);
12439         }
12440         var td = document.createElement("td");
12441         this.tr.insertBefore(td, this.tr.childNodes[index]);
12442         item.render(td);
12443         this.items.insert(index, item);
12444         return item;
12445     },
12446     
12447     /**
12448      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12449      * @param {Object} config
12450      * @return {Roo.Toolbar.Item} The element's item
12451      */
12452     addDom : function(config, returnEl){
12453         var td = this.nextBlock();
12454         Roo.DomHelper.overwrite(td, config);
12455         var ti = new Roo.Toolbar.Item(td.firstChild);
12456         ti.render(td);
12457         this.items.add(ti);
12458         return ti;
12459     },
12460
12461     /**
12462      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12463      * @type Roo.util.MixedCollection  
12464      */
12465     fields : false,
12466     
12467     /**
12468      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12469      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12470      * @param {Roo.form.Field} field
12471      * @return {Roo.ToolbarItem}
12472      */
12473      
12474       
12475     addField : function(field) {
12476         if (!this.fields) {
12477             var autoId = 0;
12478             this.fields = new Roo.util.MixedCollection(false, function(o){
12479                 return o.id || ("item" + (++autoId));
12480             });
12481
12482         }
12483         
12484         var td = this.nextBlock();
12485         field.render(td);
12486         var ti = new Roo.Toolbar.Item(td.firstChild);
12487         ti.render(td);
12488         this.items.add(ti);
12489         this.fields.add(field);
12490         return ti;
12491     },
12492     /**
12493      * Hide the toolbar
12494      * @method hide
12495      */
12496      
12497       
12498     hide : function()
12499     {
12500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12501         this.el.child('div').hide();
12502     },
12503     /**
12504      * Show the toolbar
12505      * @method show
12506      */
12507     show : function()
12508     {
12509         this.el.child('div').show();
12510     },
12511       
12512     // private
12513     nextBlock : function(){
12514         var td = document.createElement("td");
12515         this.tr.appendChild(td);
12516         return td;
12517     },
12518
12519     // private
12520     destroy : function(){
12521         if(this.items){ // rendered?
12522             Roo.destroy.apply(Roo, this.items.items);
12523         }
12524         if(this.fields){ // rendered?
12525             Roo.destroy.apply(Roo, this.fields.items);
12526         }
12527         Roo.Element.uncache(this.el, this.tr);
12528     }
12529 };
12530
12531 /**
12532  * @class Roo.Toolbar.Item
12533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12534  * @constructor
12535  * Creates a new Item
12536  * @param {HTMLElement} el 
12537  */
12538 Roo.Toolbar.Item = function(el){
12539     this.el = Roo.getDom(el);
12540     this.id = Roo.id(this.el);
12541     this.hidden = false;
12542 };
12543
12544 Roo.Toolbar.Item.prototype = {
12545     
12546     /**
12547      * Get this item's HTML Element
12548      * @return {HTMLElement}
12549      */
12550     getEl : function(){
12551        return this.el;  
12552     },
12553
12554     // private
12555     render : function(td){
12556         this.td = td;
12557         td.appendChild(this.el);
12558     },
12559     
12560     /**
12561      * Removes and destroys this item.
12562      */
12563     destroy : function(){
12564         this.td.parentNode.removeChild(this.td);
12565     },
12566     
12567     /**
12568      * Shows this item.
12569      */
12570     show: function(){
12571         this.hidden = false;
12572         this.td.style.display = "";
12573     },
12574     
12575     /**
12576      * Hides this item.
12577      */
12578     hide: function(){
12579         this.hidden = true;
12580         this.td.style.display = "none";
12581     },
12582     
12583     /**
12584      * Convenience function for boolean show/hide.
12585      * @param {Boolean} visible true to show/false to hide
12586      */
12587     setVisible: function(visible){
12588         if(visible) {
12589             this.show();
12590         }else{
12591             this.hide();
12592         }
12593     },
12594     
12595     /**
12596      * Try to focus this item.
12597      */
12598     focus : function(){
12599         Roo.fly(this.el).focus();
12600     },
12601     
12602     /**
12603      * Disables this item.
12604      */
12605     disable : function(){
12606         Roo.fly(this.td).addClass("x-item-disabled");
12607         this.disabled = true;
12608         this.el.disabled = true;
12609     },
12610     
12611     /**
12612      * Enables this item.
12613      */
12614     enable : function(){
12615         Roo.fly(this.td).removeClass("x-item-disabled");
12616         this.disabled = false;
12617         this.el.disabled = false;
12618     }
12619 };
12620
12621
12622 /**
12623  * @class Roo.Toolbar.Separator
12624  * @extends Roo.Toolbar.Item
12625  * A simple toolbar separator class
12626  * @constructor
12627  * Creates a new Separator
12628  */
12629 Roo.Toolbar.Separator = function(){
12630     var s = document.createElement("span");
12631     s.className = "ytb-sep";
12632     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12633 };
12634 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12635     enable:Roo.emptyFn,
12636     disable:Roo.emptyFn,
12637     focus:Roo.emptyFn
12638 });
12639
12640 /**
12641  * @class Roo.Toolbar.Spacer
12642  * @extends Roo.Toolbar.Item
12643  * A simple element that adds extra horizontal space to a toolbar.
12644  * @constructor
12645  * Creates a new Spacer
12646  */
12647 Roo.Toolbar.Spacer = function(){
12648     var s = document.createElement("div");
12649     s.className = "ytb-spacer";
12650     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12651 };
12652 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12653     enable:Roo.emptyFn,
12654     disable:Roo.emptyFn,
12655     focus:Roo.emptyFn
12656 });
12657
12658 /**
12659  * @class Roo.Toolbar.Fill
12660  * @extends Roo.Toolbar.Spacer
12661  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12662  * @constructor
12663  * Creates a new Spacer
12664  */
12665 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12666     // private
12667     render : function(td){
12668         td.style.width = '100%';
12669         Roo.Toolbar.Fill.superclass.render.call(this, td);
12670     }
12671 });
12672
12673 /**
12674  * @class Roo.Toolbar.TextItem
12675  * @extends Roo.Toolbar.Item
12676  * A simple class that renders text directly into a toolbar.
12677  * @constructor
12678  * Creates a new TextItem
12679  * @param {String} text
12680  */
12681 Roo.Toolbar.TextItem = function(text){
12682     if (typeof(text) == 'object') {
12683         text = text.text;
12684     }
12685     var s = document.createElement("span");
12686     s.className = "ytb-text";
12687     s.innerHTML = text;
12688     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12689 };
12690 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12691     enable:Roo.emptyFn,
12692     disable:Roo.emptyFn,
12693     focus:Roo.emptyFn
12694 });
12695
12696 /**
12697  * @class Roo.Toolbar.Button
12698  * @extends Roo.Button
12699  * A button that renders into a toolbar.
12700  * @constructor
12701  * Creates a new Button
12702  * @param {Object} config A standard {@link Roo.Button} config object
12703  */
12704 Roo.Toolbar.Button = function(config){
12705     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12706 };
12707 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12708     render : function(td){
12709         this.td = td;
12710         Roo.Toolbar.Button.superclass.render.call(this, td);
12711     },
12712     
12713     /**
12714      * Removes and destroys this button
12715      */
12716     destroy : function(){
12717         Roo.Toolbar.Button.superclass.destroy.call(this);
12718         this.td.parentNode.removeChild(this.td);
12719     },
12720     
12721     /**
12722      * Shows this button
12723      */
12724     show: function(){
12725         this.hidden = false;
12726         this.td.style.display = "";
12727     },
12728     
12729     /**
12730      * Hides this button
12731      */
12732     hide: function(){
12733         this.hidden = true;
12734         this.td.style.display = "none";
12735     },
12736
12737     /**
12738      * Disables this item
12739      */
12740     disable : function(){
12741         Roo.fly(this.td).addClass("x-item-disabled");
12742         this.disabled = true;
12743     },
12744
12745     /**
12746      * Enables this item
12747      */
12748     enable : function(){
12749         Roo.fly(this.td).removeClass("x-item-disabled");
12750         this.disabled = false;
12751     }
12752 });
12753 // backwards compat
12754 Roo.ToolbarButton = Roo.Toolbar.Button;
12755
12756 /**
12757  * @class Roo.Toolbar.SplitButton
12758  * @extends Roo.SplitButton
12759  * A menu button that renders into a toolbar.
12760  * @constructor
12761  * Creates a new SplitButton
12762  * @param {Object} config A standard {@link Roo.SplitButton} config object
12763  */
12764 Roo.Toolbar.SplitButton = function(config){
12765     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12766 };
12767 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12768     render : function(td){
12769         this.td = td;
12770         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12771     },
12772     
12773     /**
12774      * Removes and destroys this button
12775      */
12776     destroy : function(){
12777         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12778         this.td.parentNode.removeChild(this.td);
12779     },
12780     
12781     /**
12782      * Shows this button
12783      */
12784     show: function(){
12785         this.hidden = false;
12786         this.td.style.display = "";
12787     },
12788     
12789     /**
12790      * Hides this button
12791      */
12792     hide: function(){
12793         this.hidden = true;
12794         this.td.style.display = "none";
12795     }
12796 });
12797
12798 // backwards compat
12799 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809  
12810 /**
12811  * @class Roo.PagingToolbar
12812  * @extends Roo.Toolbar
12813  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12814  * @constructor
12815  * Create a new PagingToolbar
12816  * @param {Object} config The config object
12817  */
12818 Roo.PagingToolbar = function(el, ds, config)
12819 {
12820     // old args format still supported... - xtype is prefered..
12821     if (typeof(el) == 'object' && el.xtype) {
12822         // created from xtype...
12823         config = el;
12824         ds = el.dataSource;
12825         el = config.container;
12826     }
12827     var items = [];
12828     if (config.items) {
12829         items = config.items;
12830         config.items = [];
12831     }
12832     
12833     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12834     this.ds = ds;
12835     this.cursor = 0;
12836     this.renderButtons(this.el);
12837     this.bind(ds);
12838     
12839     // supprot items array.
12840    
12841     Roo.each(items, function(e) {
12842         this.add(Roo.factory(e));
12843     },this);
12844     
12845 };
12846
12847 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12848     /**
12849      * @cfg {Roo.data.Store} dataSource
12850      * The underlying data store providing the paged data
12851      */
12852     /**
12853      * @cfg {String/HTMLElement/Element} container
12854      * container The id or element that will contain the toolbar
12855      */
12856     /**
12857      * @cfg {Boolean} displayInfo
12858      * True to display the displayMsg (defaults to false)
12859      */
12860     /**
12861      * @cfg {Number} pageSize
12862      * The number of records to display per page (defaults to 20)
12863      */
12864     pageSize: 20,
12865     /**
12866      * @cfg {String} displayMsg
12867      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12868      */
12869     displayMsg : 'Displaying {0} - {1} of {2}',
12870     /**
12871      * @cfg {String} emptyMsg
12872      * The message to display when no records are found (defaults to "No data to display")
12873      */
12874     emptyMsg : 'No data to display',
12875     /**
12876      * Customizable piece of the default paging text (defaults to "Page")
12877      * @type String
12878      */
12879     beforePageText : "Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "of %0")
12882      * @type String
12883      */
12884     afterPageText : "of {0}",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "First Page")
12887      * @type String
12888      */
12889     firstText : "First Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Previous Page")
12892      * @type String
12893      */
12894     prevText : "Previous Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Next Page")
12897      * @type String
12898      */
12899     nextText : "Next Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Last Page")
12902      * @type String
12903      */
12904     lastText : "Last Page",
12905     /**
12906      * Customizable piece of the default paging text (defaults to "Refresh")
12907      * @type String
12908      */
12909     refreshText : "Refresh",
12910
12911     // private
12912     renderButtons : function(el){
12913         Roo.PagingToolbar.superclass.render.call(this, el);
12914         this.first = this.addButton({
12915             tooltip: this.firstText,
12916             cls: "x-btn-icon x-grid-page-first",
12917             disabled: true,
12918             handler: this.onClick.createDelegate(this, ["first"])
12919         });
12920         this.prev = this.addButton({
12921             tooltip: this.prevText,
12922             cls: "x-btn-icon x-grid-page-prev",
12923             disabled: true,
12924             handler: this.onClick.createDelegate(this, ["prev"])
12925         });
12926         //this.addSeparator();
12927         this.add(this.beforePageText);
12928         this.field = Roo.get(this.addDom({
12929            tag: "input",
12930            type: "text",
12931            size: "3",
12932            value: "1",
12933            cls: "x-grid-page-number"
12934         }).el);
12935         this.field.on("keydown", this.onPagingKeydown, this);
12936         this.field.on("focus", function(){this.dom.select();});
12937         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12938         this.field.setHeight(18);
12939         //this.addSeparator();
12940         this.next = this.addButton({
12941             tooltip: this.nextText,
12942             cls: "x-btn-icon x-grid-page-next",
12943             disabled: true,
12944             handler: this.onClick.createDelegate(this, ["next"])
12945         });
12946         this.last = this.addButton({
12947             tooltip: this.lastText,
12948             cls: "x-btn-icon x-grid-page-last",
12949             disabled: true,
12950             handler: this.onClick.createDelegate(this, ["last"])
12951         });
12952         //this.addSeparator();
12953         this.loading = this.addButton({
12954             tooltip: this.refreshText,
12955             cls: "x-btn-icon x-grid-loading",
12956             handler: this.onClick.createDelegate(this, ["refresh"])
12957         });
12958
12959         if(this.displayInfo){
12960             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12961         }
12962     },
12963
12964     // private
12965     updateInfo : function(){
12966         if(this.displayEl){
12967             var count = this.ds.getCount();
12968             var msg = count == 0 ?
12969                 this.emptyMsg :
12970                 String.format(
12971                     this.displayMsg,
12972                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12973                 );
12974             this.displayEl.update(msg);
12975         }
12976     },
12977
12978     // private
12979     onLoad : function(ds, r, o){
12980        this.cursor = o.params ? o.params.start : 0;
12981        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12982
12983        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12984        this.field.dom.value = ap;
12985        this.first.setDisabled(ap == 1);
12986        this.prev.setDisabled(ap == 1);
12987        this.next.setDisabled(ap == ps);
12988        this.last.setDisabled(ap == ps);
12989        this.loading.enable();
12990        this.updateInfo();
12991     },
12992
12993     // private
12994     getPageData : function(){
12995         var total = this.ds.getTotalCount();
12996         return {
12997             total : total,
12998             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12999             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13000         };
13001     },
13002
13003     // private
13004     onLoadError : function(){
13005         this.loading.enable();
13006     },
13007
13008     // private
13009     onPagingKeydown : function(e){
13010         var k = e.getKey();
13011         var d = this.getPageData();
13012         if(k == e.RETURN){
13013             var v = this.field.dom.value, pageNum;
13014             if(!v || isNaN(pageNum = parseInt(v, 10))){
13015                 this.field.dom.value = d.activePage;
13016                 return;
13017             }
13018             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13020             e.stopEvent();
13021         }
13022         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))
13023         {
13024           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13025           this.field.dom.value = pageNum;
13026           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13027           e.stopEvent();
13028         }
13029         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13030         {
13031           var v = this.field.dom.value, pageNum; 
13032           var increment = (e.shiftKey) ? 10 : 1;
13033           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13034             increment *= -1;
13035           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13036             this.field.dom.value = d.activePage;
13037             return;
13038           }
13039           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13040           {
13041             this.field.dom.value = parseInt(v, 10) + increment;
13042             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13043             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13044           }
13045           e.stopEvent();
13046         }
13047     },
13048
13049     // private
13050     beforeLoad : function(){
13051         if(this.loading){
13052             this.loading.disable();
13053         }
13054     },
13055
13056     // private
13057     onClick : function(which){
13058         var ds = this.ds;
13059         switch(which){
13060             case "first":
13061                 ds.load({params:{start: 0, limit: this.pageSize}});
13062             break;
13063             case "prev":
13064                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13065             break;
13066             case "next":
13067                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13068             break;
13069             case "last":
13070                 var total = ds.getTotalCount();
13071                 var extra = total % this.pageSize;
13072                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13073                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13074             break;
13075             case "refresh":
13076                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13077             break;
13078         }
13079     },
13080
13081     /**
13082      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13083      * @param {Roo.data.Store} store The data store to unbind
13084      */
13085     unbind : function(ds){
13086         ds.un("beforeload", this.beforeLoad, this);
13087         ds.un("load", this.onLoad, this);
13088         ds.un("loadexception", this.onLoadError, this);
13089         ds.un("remove", this.updateInfo, this);
13090         ds.un("add", this.updateInfo, this);
13091         this.ds = undefined;
13092     },
13093
13094     /**
13095      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13096      * @param {Roo.data.Store} store The data store to bind
13097      */
13098     bind : function(ds){
13099         ds.on("beforeload", this.beforeLoad, this);
13100         ds.on("load", this.onLoad, this);
13101         ds.on("loadexception", this.onLoadError, this);
13102         ds.on("remove", this.updateInfo, this);
13103         ds.on("add", this.updateInfo, this);
13104         this.ds = ds;
13105     }
13106 });/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117 /**
13118  * @class Roo.Resizable
13119  * @extends Roo.util.Observable
13120  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13121  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13122  * 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
13123  * the element will be wrapped for you automatically.</p>
13124  * <p>Here is the list of valid resize handles:</p>
13125  * <pre>
13126 Value   Description
13127 ------  -------------------
13128  'n'     north
13129  's'     south
13130  'e'     east
13131  'w'     west
13132  'nw'    northwest
13133  'sw'    southwest
13134  'se'    southeast
13135  'ne'    northeast
13136  'hd'    horizontal drag
13137  'all'   all
13138 </pre>
13139  * <p>Here's an example showing the creation of a typical Resizable:</p>
13140  * <pre><code>
13141 var resizer = new Roo.Resizable("element-id", {
13142     handles: 'all',
13143     minWidth: 200,
13144     minHeight: 100,
13145     maxWidth: 500,
13146     maxHeight: 400,
13147     pinned: true
13148 });
13149 resizer.on("resize", myHandler);
13150 </code></pre>
13151  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13152  * resizer.east.setDisplayed(false);</p>
13153  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13154  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13155  * resize operation's new size (defaults to [0, 0])
13156  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13157  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13158  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13159  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13160  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13161  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13162  * @cfg {Number} width The width of the element in pixels (defaults to null)
13163  * @cfg {Number} height The height of the element in pixels (defaults to null)
13164  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13165  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13166  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13167  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13168  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13169  * in favor of the handles config option (defaults to false)
13170  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13171  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13172  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13173  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13174  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13175  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13176  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13177  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13178  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13179  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13180  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13181  * @constructor
13182  * Create a new resizable component
13183  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13184  * @param {Object} config configuration options
13185   */
13186 Roo.Resizable = function(el, config)
13187 {
13188     this.el = Roo.get(el);
13189
13190     if(config && config.wrap){
13191         config.resizeChild = this.el;
13192         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13193         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13194         this.el.setStyle("overflow", "hidden");
13195         this.el.setPositioning(config.resizeChild.getPositioning());
13196         config.resizeChild.clearPositioning();
13197         if(!config.width || !config.height){
13198             var csize = config.resizeChild.getSize();
13199             this.el.setSize(csize.width, csize.height);
13200         }
13201         if(config.pinned && !config.adjustments){
13202             config.adjustments = "auto";
13203         }
13204     }
13205
13206     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13207     this.proxy.unselectable();
13208     this.proxy.enableDisplayMode('block');
13209
13210     Roo.apply(this, config);
13211
13212     if(this.pinned){
13213         this.disableTrackOver = true;
13214         this.el.addClass("x-resizable-pinned");
13215     }
13216     // if the element isn't positioned, make it relative
13217     var position = this.el.getStyle("position");
13218     if(position != "absolute" && position != "fixed"){
13219         this.el.setStyle("position", "relative");
13220     }
13221     if(!this.handles){ // no handles passed, must be legacy style
13222         this.handles = 's,e,se';
13223         if(this.multiDirectional){
13224             this.handles += ',n,w';
13225         }
13226     }
13227     if(this.handles == "all"){
13228         this.handles = "n s e w ne nw se sw";
13229     }
13230     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13231     var ps = Roo.Resizable.positions;
13232     for(var i = 0, len = hs.length; i < len; i++){
13233         if(hs[i] && ps[hs[i]]){
13234             var pos = ps[hs[i]];
13235             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13236         }
13237     }
13238     // legacy
13239     this.corner = this.southeast;
13240     
13241     // updateBox = the box can move..
13242     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13243         this.updateBox = true;
13244     }
13245
13246     this.activeHandle = null;
13247
13248     if(this.resizeChild){
13249         if(typeof this.resizeChild == "boolean"){
13250             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13251         }else{
13252             this.resizeChild = Roo.get(this.resizeChild, true);
13253         }
13254     }
13255     
13256     if(this.adjustments == "auto"){
13257         var rc = this.resizeChild;
13258         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13259         if(rc && (hw || hn)){
13260             rc.position("relative");
13261             rc.setLeft(hw ? hw.el.getWidth() : 0);
13262             rc.setTop(hn ? hn.el.getHeight() : 0);
13263         }
13264         this.adjustments = [
13265             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13266             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13267         ];
13268     }
13269
13270     if(this.draggable){
13271         this.dd = this.dynamic ?
13272             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13273         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13274     }
13275
13276     // public events
13277     this.addEvents({
13278         /**
13279          * @event beforeresize
13280          * Fired before resize is allowed. Set enabled to false to cancel resize.
13281          * @param {Roo.Resizable} this
13282          * @param {Roo.EventObject} e The mousedown event
13283          */
13284         "beforeresize" : true,
13285         /**
13286          * @event resize
13287          * Fired after a resize.
13288          * @param {Roo.Resizable} this
13289          * @param {Number} width The new width
13290          * @param {Number} height The new height
13291          * @param {Roo.EventObject} e The mouseup event
13292          */
13293         "resize" : true
13294     });
13295
13296     if(this.width !== null && this.height !== null){
13297         this.resizeTo(this.width, this.height);
13298     }else{
13299         this.updateChildSize();
13300     }
13301     if(Roo.isIE){
13302         this.el.dom.style.zoom = 1;
13303     }
13304     Roo.Resizable.superclass.constructor.call(this);
13305 };
13306
13307 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13308         resizeChild : false,
13309         adjustments : [0, 0],
13310         minWidth : 5,
13311         minHeight : 5,
13312         maxWidth : 10000,
13313         maxHeight : 10000,
13314         enabled : true,
13315         animate : false,
13316         duration : .35,
13317         dynamic : false,
13318         handles : false,
13319         multiDirectional : false,
13320         disableTrackOver : false,
13321         easing : 'easeOutStrong',
13322         widthIncrement : 0,
13323         heightIncrement : 0,
13324         pinned : false,
13325         width : null,
13326         height : null,
13327         preserveRatio : false,
13328         transparent: false,
13329         minX: 0,
13330         minY: 0,
13331         draggable: false,
13332
13333         /**
13334          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13335          */
13336         constrainTo: undefined,
13337         /**
13338          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13339          */
13340         resizeRegion: undefined,
13341
13342
13343     /**
13344      * Perform a manual resize
13345      * @param {Number} width
13346      * @param {Number} height
13347      */
13348     resizeTo : function(width, height){
13349         this.el.setSize(width, height);
13350         this.updateChildSize();
13351         this.fireEvent("resize", this, width, height, null);
13352     },
13353
13354     // private
13355     startSizing : function(e, handle){
13356         this.fireEvent("beforeresize", this, e);
13357         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13358
13359             if(!this.overlay){
13360                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13361                 this.overlay.unselectable();
13362                 this.overlay.enableDisplayMode("block");
13363                 this.overlay.on("mousemove", this.onMouseMove, this);
13364                 this.overlay.on("mouseup", this.onMouseUp, this);
13365             }
13366             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13367
13368             this.resizing = true;
13369             this.startBox = this.el.getBox();
13370             this.startPoint = e.getXY();
13371             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13372                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13373
13374             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13375             this.overlay.show();
13376
13377             if(this.constrainTo) {
13378                 var ct = Roo.get(this.constrainTo);
13379                 this.resizeRegion = ct.getRegion().adjust(
13380                     ct.getFrameWidth('t'),
13381                     ct.getFrameWidth('l'),
13382                     -ct.getFrameWidth('b'),
13383                     -ct.getFrameWidth('r')
13384                 );
13385             }
13386
13387             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13388             this.proxy.show();
13389             this.proxy.setBox(this.startBox);
13390             if(!this.dynamic){
13391                 this.proxy.setStyle('visibility', 'visible');
13392             }
13393         }
13394     },
13395
13396     // private
13397     onMouseDown : function(handle, e){
13398         if(this.enabled){
13399             e.stopEvent();
13400             this.activeHandle = handle;
13401             this.startSizing(e, handle);
13402         }
13403     },
13404
13405     // private
13406     onMouseUp : function(e){
13407         var size = this.resizeElement();
13408         this.resizing = false;
13409         this.handleOut();
13410         this.overlay.hide();
13411         this.proxy.hide();
13412         this.fireEvent("resize", this, size.width, size.height, e);
13413     },
13414
13415     // private
13416     updateChildSize : function(){
13417         if(this.resizeChild){
13418             var el = this.el;
13419             var child = this.resizeChild;
13420             var adj = this.adjustments;
13421             if(el.dom.offsetWidth){
13422                 var b = el.getSize(true);
13423                 child.setSize(b.width+adj[0], b.height+adj[1]);
13424             }
13425             // Second call here for IE
13426             // The first call enables instant resizing and
13427             // the second call corrects scroll bars if they
13428             // exist
13429             if(Roo.isIE){
13430                 setTimeout(function(){
13431                     if(el.dom.offsetWidth){
13432                         var b = el.getSize(true);
13433                         child.setSize(b.width+adj[0], b.height+adj[1]);
13434                     }
13435                 }, 10);
13436             }
13437         }
13438     },
13439
13440     // private
13441     snap : function(value, inc, min){
13442         if(!inc || !value) return value;
13443         var newValue = value;
13444         var m = value % inc;
13445         if(m > 0){
13446             if(m > (inc/2)){
13447                 newValue = value + (inc-m);
13448             }else{
13449                 newValue = value - m;
13450             }
13451         }
13452         return Math.max(min, newValue);
13453     },
13454
13455     // private
13456     resizeElement : function(){
13457         var box = this.proxy.getBox();
13458         if(this.updateBox){
13459             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13460         }else{
13461             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13462         }
13463         this.updateChildSize();
13464         if(!this.dynamic){
13465             this.proxy.hide();
13466         }
13467         return box;
13468     },
13469
13470     // private
13471     constrain : function(v, diff, m, mx){
13472         if(v - diff < m){
13473             diff = v - m;
13474         }else if(v - diff > mx){
13475             diff = mx - v;
13476         }
13477         return diff;
13478     },
13479
13480     // private
13481     onMouseMove : function(e){
13482         if(this.enabled){
13483             try{// try catch so if something goes wrong the user doesn't get hung
13484
13485             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13486                 return;
13487             }
13488
13489             //var curXY = this.startPoint;
13490             var curSize = this.curSize || this.startBox;
13491             var x = this.startBox.x, y = this.startBox.y;
13492             var ox = x, oy = y;
13493             var w = curSize.width, h = curSize.height;
13494             var ow = w, oh = h;
13495             var mw = this.minWidth, mh = this.minHeight;
13496             var mxw = this.maxWidth, mxh = this.maxHeight;
13497             var wi = this.widthIncrement;
13498             var hi = this.heightIncrement;
13499
13500             var eventXY = e.getXY();
13501             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13502             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13503
13504             var pos = this.activeHandle.position;
13505
13506             switch(pos){
13507                 case "east":
13508                     w += diffX;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     break;
13511              
13512                 case "south":
13513                     h += diffY;
13514                     h = Math.min(Math.max(mh, h), mxh);
13515                     break;
13516                 case "southeast":
13517                     w += diffX;
13518                     h += diffY;
13519                     w = Math.min(Math.max(mw, w), mxw);
13520                     h = Math.min(Math.max(mh, h), mxh);
13521                     break;
13522                 case "north":
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "hdrag":
13528                     
13529                     if (wi) {
13530                         var adiffX = Math.abs(diffX);
13531                         var sub = (adiffX % wi); // how much 
13532                         if (sub > (wi/2)) { // far enough to snap
13533                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13534                         } else {
13535                             // remove difference.. 
13536                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13537                         }
13538                     }
13539                     x += diffX;
13540                     x = Math.max(this.minX, x);
13541                     break;
13542                 case "west":
13543                     diffX = this.constrain(w, diffX, mw, mxw);
13544                     x += diffX;
13545                     w -= diffX;
13546                     break;
13547                 case "northeast":
13548                     w += diffX;
13549                     w = Math.min(Math.max(mw, w), mxw);
13550                     diffY = this.constrain(h, diffY, mh, mxh);
13551                     y += diffY;
13552                     h -= diffY;
13553                     break;
13554                 case "northwest":
13555                     diffX = this.constrain(w, diffX, mw, mxw);
13556                     diffY = this.constrain(h, diffY, mh, mxh);
13557                     y += diffY;
13558                     h -= diffY;
13559                     x += diffX;
13560                     w -= diffX;
13561                     break;
13562                case "southwest":
13563                     diffX = this.constrain(w, diffX, mw, mxw);
13564                     h += diffY;
13565                     h = Math.min(Math.max(mh, h), mxh);
13566                     x += diffX;
13567                     w -= diffX;
13568                     break;
13569             }
13570
13571             var sw = this.snap(w, wi, mw);
13572             var sh = this.snap(h, hi, mh);
13573             if(sw != w || sh != h){
13574                 switch(pos){
13575                     case "northeast":
13576                         y -= sh - h;
13577                     break;
13578                     case "north":
13579                         y -= sh - h;
13580                         break;
13581                     case "southwest":
13582                         x -= sw - w;
13583                     break;
13584                     case "west":
13585                         x -= sw - w;
13586                         break;
13587                     case "northwest":
13588                         x -= sw - w;
13589                         y -= sh - h;
13590                     break;
13591                 }
13592                 w = sw;
13593                 h = sh;
13594             }
13595
13596             if(this.preserveRatio){
13597                 switch(pos){
13598                     case "southeast":
13599                     case "east":
13600                         h = oh * (w/ow);
13601                         h = Math.min(Math.max(mh, h), mxh);
13602                         w = ow * (h/oh);
13603                        break;
13604                     case "south":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                         break;
13609                     case "northeast":
13610                         w = ow * (h/oh);
13611                         w = Math.min(Math.max(mw, w), mxw);
13612                         h = oh * (w/ow);
13613                     break;
13614                     case "north":
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         w = Math.min(Math.max(mw, w), mxw);
13618                         h = oh * (w/ow);
13619                         x += (tw - w) / 2;
13620                         break;
13621                     case "southwest":
13622                         h = oh * (w/ow);
13623                         h = Math.min(Math.max(mh, h), mxh);
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                         break;
13628                     case "west":
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         y += (th - h) / 2;
13633                         var tw = w;
13634                         w = ow * (h/oh);
13635                         x += tw - w;
13636                        break;
13637                     case "northwest":
13638                         var tw = w;
13639                         var th = h;
13640                         h = oh * (w/ow);
13641                         h = Math.min(Math.max(mh, h), mxh);
13642                         w = ow * (h/oh);
13643                         y += th - h;
13644                         x += tw - w;
13645                        break;
13646
13647                 }
13648             }
13649             if (pos == 'hdrag') {
13650                 w = ow;
13651             }
13652             this.proxy.setBounds(x, y, w, h);
13653             if(this.dynamic){
13654                 this.resizeElement();
13655             }
13656             }catch(e){}
13657         }
13658     },
13659
13660     // private
13661     handleOver : function(){
13662         if(this.enabled){
13663             this.el.addClass("x-resizable-over");
13664         }
13665     },
13666
13667     // private
13668     handleOut : function(){
13669         if(!this.resizing){
13670             this.el.removeClass("x-resizable-over");
13671         }
13672     },
13673
13674     /**
13675      * Returns the element this component is bound to.
13676      * @return {Roo.Element}
13677      */
13678     getEl : function(){
13679         return this.el;
13680     },
13681
13682     /**
13683      * Returns the resizeChild element (or null).
13684      * @return {Roo.Element}
13685      */
13686     getResizeChild : function(){
13687         return this.resizeChild;
13688     },
13689
13690     /**
13691      * Destroys this resizable. If the element was wrapped and
13692      * removeEl is not true then the element remains.
13693      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13694      */
13695     destroy : function(removeEl){
13696         this.proxy.remove();
13697         if(this.overlay){
13698             this.overlay.removeAllListeners();
13699             this.overlay.remove();
13700         }
13701         var ps = Roo.Resizable.positions;
13702         for(var k in ps){
13703             if(typeof ps[k] != "function" && this[ps[k]]){
13704                 var h = this[ps[k]];
13705                 h.el.removeAllListeners();
13706                 h.el.remove();
13707             }
13708         }
13709         if(removeEl){
13710             this.el.update("");
13711             this.el.remove();
13712         }
13713     }
13714 });
13715
13716 // private
13717 // hash to map config positions to true positions
13718 Roo.Resizable.positions = {
13719     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13720     hd: "hdrag"
13721 };
13722
13723 // private
13724 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13725     if(!this.tpl){
13726         // only initialize the template if resizable is used
13727         var tpl = Roo.DomHelper.createTemplate(
13728             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13729         );
13730         tpl.compile();
13731         Roo.Resizable.Handle.prototype.tpl = tpl;
13732     }
13733     this.position = pos;
13734     this.rz = rz;
13735     // show north drag fro topdra
13736     var handlepos = pos == 'hdrag' ? 'north' : pos;
13737     
13738     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13739     if (pos == 'hdrag') {
13740         this.el.setStyle('cursor', 'pointer');
13741     }
13742     this.el.unselectable();
13743     if(transparent){
13744         this.el.setOpacity(0);
13745     }
13746     this.el.on("mousedown", this.onMouseDown, this);
13747     if(!disableTrackOver){
13748         this.el.on("mouseover", this.onMouseOver, this);
13749         this.el.on("mouseout", this.onMouseOut, this);
13750     }
13751 };
13752
13753 // private
13754 Roo.Resizable.Handle.prototype = {
13755     afterResize : function(rz){
13756         // do nothing
13757     },
13758     // private
13759     onMouseDown : function(e){
13760         this.rz.onMouseDown(this, e);
13761     },
13762     // private
13763     onMouseOver : function(e){
13764         this.rz.handleOver(this, e);
13765     },
13766     // private
13767     onMouseOut : function(e){
13768         this.rz.handleOut(this, e);
13769     }
13770 };/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780
13781 /**
13782  * @class Roo.Editor
13783  * @extends Roo.Component
13784  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13785  * @constructor
13786  * Create a new Editor
13787  * @param {Roo.form.Field} field The Field object (or descendant)
13788  * @param {Object} config The config object
13789  */
13790 Roo.Editor = function(field, config){
13791     Roo.Editor.superclass.constructor.call(this, config);
13792     this.field = field;
13793     this.addEvents({
13794         /**
13795              * @event beforestartedit
13796              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13797              * false from the handler of this event.
13798              * @param {Editor} this
13799              * @param {Roo.Element} boundEl The underlying element bound to this editor
13800              * @param {Mixed} value The field value being set
13801              */
13802         "beforestartedit" : true,
13803         /**
13804              * @event startedit
13805              * Fires when this editor is displayed
13806              * @param {Roo.Element} boundEl The underlying element bound to this editor
13807              * @param {Mixed} value The starting field value
13808              */
13809         "startedit" : true,
13810         /**
13811              * @event beforecomplete
13812              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13813              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13814              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13815              * event will not fire since no edit actually occurred.
13816              * @param {Editor} this
13817              * @param {Mixed} value The current field value
13818              * @param {Mixed} startValue The original field value
13819              */
13820         "beforecomplete" : true,
13821         /**
13822              * @event complete
13823              * Fires after editing is complete and any changed value has been written to the underlying field.
13824              * @param {Editor} this
13825              * @param {Mixed} value The current field value
13826              * @param {Mixed} startValue The original field value
13827              */
13828         "complete" : true,
13829         /**
13830          * @event specialkey
13831          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13832          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13833          * @param {Roo.form.Field} this
13834          * @param {Roo.EventObject} e The event object
13835          */
13836         "specialkey" : true
13837     });
13838 };
13839
13840 Roo.extend(Roo.Editor, Roo.Component, {
13841     /**
13842      * @cfg {Boolean/String} autosize
13843      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13844      * or "height" to adopt the height only (defaults to false)
13845      */
13846     /**
13847      * @cfg {Boolean} revertInvalid
13848      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13849      * validation fails (defaults to true)
13850      */
13851     /**
13852      * @cfg {Boolean} ignoreNoChange
13853      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13854      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13855      * will never be ignored.
13856      */
13857     /**
13858      * @cfg {Boolean} hideEl
13859      * False to keep the bound element visible while the editor is displayed (defaults to true)
13860      */
13861     /**
13862      * @cfg {Mixed} value
13863      * The data value of the underlying field (defaults to "")
13864      */
13865     value : "",
13866     /**
13867      * @cfg {String} alignment
13868      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13869      */
13870     alignment: "c-c?",
13871     /**
13872      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13873      * for bottom-right shadow (defaults to "frame")
13874      */
13875     shadow : "frame",
13876     /**
13877      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13878      */
13879     constrain : false,
13880     /**
13881      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13882      */
13883     completeOnEnter : false,
13884     /**
13885      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13886      */
13887     cancelOnEsc : false,
13888     /**
13889      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13890      */
13891     updateEl : false,
13892
13893     // private
13894     onRender : function(ct, position){
13895         this.el = new Roo.Layer({
13896             shadow: this.shadow,
13897             cls: "x-editor",
13898             parentEl : ct,
13899             shim : this.shim,
13900             shadowOffset:4,
13901             id: this.id,
13902             constrain: this.constrain
13903         });
13904         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13905         if(this.field.msgTarget != 'title'){
13906             this.field.msgTarget = 'qtip';
13907         }
13908         this.field.render(this.el);
13909         if(Roo.isGecko){
13910             this.field.el.dom.setAttribute('autocomplete', 'off');
13911         }
13912         this.field.on("specialkey", this.onSpecialKey, this);
13913         if(this.swallowKeys){
13914             this.field.el.swallowEvent(['keydown','keypress']);
13915         }
13916         this.field.show();
13917         this.field.on("blur", this.onBlur, this);
13918         if(this.field.grow){
13919             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13920         }
13921     },
13922
13923     onSpecialKey : function(field, e)
13924     {
13925         //Roo.log('editor onSpecialKey');
13926         if(this.completeOnEnter && e.getKey() == e.ENTER){
13927             e.stopEvent();
13928             this.completeEdit();
13929             return;
13930         }
13931         // do not fire special key otherwise it might hide close the editor...
13932         if(e.getKey() == e.ENTER){    
13933             return;
13934         }
13935         if(this.cancelOnEsc && e.getKey() == e.ESC){
13936             this.cancelEdit();
13937             return;
13938         } 
13939         this.fireEvent('specialkey', field, e);
13940     
13941     },
13942
13943     /**
13944      * Starts the editing process and shows the editor.
13945      * @param {String/HTMLElement/Element} el The element to edit
13946      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13947       * to the innerHTML of el.
13948      */
13949     startEdit : function(el, value){
13950         if(this.editing){
13951             this.completeEdit();
13952         }
13953         this.boundEl = Roo.get(el);
13954         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13955         if(!this.rendered){
13956             this.render(this.parentEl || document.body);
13957         }
13958         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13959             return;
13960         }
13961         this.startValue = v;
13962         this.field.setValue(v);
13963         if(this.autoSize){
13964             var sz = this.boundEl.getSize();
13965             switch(this.autoSize){
13966                 case "width":
13967                 this.setSize(sz.width,  "");
13968                 break;
13969                 case "height":
13970                 this.setSize("",  sz.height);
13971                 break;
13972                 default:
13973                 this.setSize(sz.width,  sz.height);
13974             }
13975         }
13976         this.el.alignTo(this.boundEl, this.alignment);
13977         this.editing = true;
13978         if(Roo.QuickTips){
13979             Roo.QuickTips.disable();
13980         }
13981         this.show();
13982     },
13983
13984     /**
13985      * Sets the height and width of this editor.
13986      * @param {Number} width The new width
13987      * @param {Number} height The new height
13988      */
13989     setSize : function(w, h){
13990         this.field.setSize(w, h);
13991         if(this.el){
13992             this.el.sync();
13993         }
13994     },
13995
13996     /**
13997      * Realigns the editor to the bound field based on the current alignment config value.
13998      */
13999     realign : function(){
14000         this.el.alignTo(this.boundEl, this.alignment);
14001     },
14002
14003     /**
14004      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14005      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14006      */
14007     completeEdit : function(remainVisible){
14008         if(!this.editing){
14009             return;
14010         }
14011         var v = this.getValue();
14012         if(this.revertInvalid !== false && !this.field.isValid()){
14013             v = this.startValue;
14014             this.cancelEdit(true);
14015         }
14016         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14017             this.editing = false;
14018             this.hide();
14019             return;
14020         }
14021         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14022             this.editing = false;
14023             if(this.updateEl && this.boundEl){
14024                 this.boundEl.update(v);
14025             }
14026             if(remainVisible !== true){
14027                 this.hide();
14028             }
14029             this.fireEvent("complete", this, v, this.startValue);
14030         }
14031     },
14032
14033     // private
14034     onShow : function(){
14035         this.el.show();
14036         if(this.hideEl !== false){
14037             this.boundEl.hide();
14038         }
14039         this.field.show();
14040         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14041             this.fixIEFocus = true;
14042             this.deferredFocus.defer(50, this);
14043         }else{
14044             this.field.focus();
14045         }
14046         this.fireEvent("startedit", this.boundEl, this.startValue);
14047     },
14048
14049     deferredFocus : function(){
14050         if(this.editing){
14051             this.field.focus();
14052         }
14053     },
14054
14055     /**
14056      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14057      * reverted to the original starting value.
14058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14059      * cancel (defaults to false)
14060      */
14061     cancelEdit : function(remainVisible){
14062         if(this.editing){
14063             this.setValue(this.startValue);
14064             if(remainVisible !== true){
14065                 this.hide();
14066             }
14067         }
14068     },
14069
14070     // private
14071     onBlur : function(){
14072         if(this.allowBlur !== true && this.editing){
14073             this.completeEdit();
14074         }
14075     },
14076
14077     // private
14078     onHide : function(){
14079         if(this.editing){
14080             this.completeEdit();
14081             return;
14082         }
14083         this.field.blur();
14084         if(this.field.collapse){
14085             this.field.collapse();
14086         }
14087         this.el.hide();
14088         if(this.hideEl !== false){
14089             this.boundEl.show();
14090         }
14091         if(Roo.QuickTips){
14092             Roo.QuickTips.enable();
14093         }
14094     },
14095
14096     /**
14097      * Sets the data value of the editor
14098      * @param {Mixed} value Any valid value supported by the underlying field
14099      */
14100     setValue : function(v){
14101         this.field.setValue(v);
14102     },
14103
14104     /**
14105      * Gets the data value of the editor
14106      * @return {Mixed} The data value
14107      */
14108     getValue : function(){
14109         return this.field.getValue();
14110     }
14111 });/*
14112  * Based on:
14113  * Ext JS Library 1.1.1
14114  * Copyright(c) 2006-2007, Ext JS, LLC.
14115  *
14116  * Originally Released Under LGPL - original licence link has changed is not relivant.
14117  *
14118  * Fork - LGPL
14119  * <script type="text/javascript">
14120  */
14121  
14122 /**
14123  * @class Roo.BasicDialog
14124  * @extends Roo.util.Observable
14125  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14126  * <pre><code>
14127 var dlg = new Roo.BasicDialog("my-dlg", {
14128     height: 200,
14129     width: 300,
14130     minHeight: 100,
14131     minWidth: 150,
14132     modal: true,
14133     proxyDrag: true,
14134     shadow: true
14135 });
14136 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14137 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14138 dlg.addButton('Cancel', dlg.hide, dlg);
14139 dlg.show();
14140 </code></pre>
14141   <b>A Dialog should always be a direct child of the body element.</b>
14142  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14143  * @cfg {String} title Default text to display in the title bar (defaults to null)
14144  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14145  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14146  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14147  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14148  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14149  * (defaults to null with no animation)
14150  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14151  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14152  * property for valid values (defaults to 'all')
14153  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14154  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14155  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14156  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14157  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14158  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14159  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14160  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14161  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14162  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14163  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14164  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14165  * draggable = true (defaults to false)
14166  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14167  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14168  * shadow (defaults to false)
14169  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14170  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14171  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14172  * @cfg {Array} buttons Array of buttons
14173  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14174  * @constructor
14175  * Create a new BasicDialog.
14176  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14177  * @param {Object} config Configuration options
14178  */
14179 Roo.BasicDialog = function(el, config){
14180     this.el = Roo.get(el);
14181     var dh = Roo.DomHelper;
14182     if(!this.el && config && config.autoCreate){
14183         if(typeof config.autoCreate == "object"){
14184             if(!config.autoCreate.id){
14185                 config.autoCreate.id = el;
14186             }
14187             this.el = dh.append(document.body,
14188                         config.autoCreate, true);
14189         }else{
14190             this.el = dh.append(document.body,
14191                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14192         }
14193     }
14194     el = this.el;
14195     el.setDisplayed(true);
14196     el.hide = this.hideAction;
14197     this.id = el.id;
14198     el.addClass("x-dlg");
14199
14200     Roo.apply(this, config);
14201
14202     this.proxy = el.createProxy("x-dlg-proxy");
14203     this.proxy.hide = this.hideAction;
14204     this.proxy.setOpacity(.5);
14205     this.proxy.hide();
14206
14207     if(config.width){
14208         el.setWidth(config.width);
14209     }
14210     if(config.height){
14211         el.setHeight(config.height);
14212     }
14213     this.size = el.getSize();
14214     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14215         this.xy = [config.x,config.y];
14216     }else{
14217         this.xy = el.getCenterXY(true);
14218     }
14219     /** The header element @type Roo.Element */
14220     this.header = el.child("> .x-dlg-hd");
14221     /** The body element @type Roo.Element */
14222     this.body = el.child("> .x-dlg-bd");
14223     /** The footer element @type Roo.Element */
14224     this.footer = el.child("> .x-dlg-ft");
14225
14226     if(!this.header){
14227         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14228     }
14229     if(!this.body){
14230         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14231     }
14232
14233     this.header.unselectable();
14234     if(this.title){
14235         this.header.update(this.title);
14236     }
14237     // this element allows the dialog to be focused for keyboard event
14238     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14239     this.focusEl.swallowEvent("click", true);
14240
14241     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14242
14243     // wrap the body and footer for special rendering
14244     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14245     if(this.footer){
14246         this.bwrap.dom.appendChild(this.footer.dom);
14247     }
14248
14249     this.bg = this.el.createChild({
14250         tag: "div", cls:"x-dlg-bg",
14251         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14252     });
14253     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14254
14255
14256     if(this.autoScroll !== false && !this.autoTabs){
14257         this.body.setStyle("overflow", "auto");
14258     }
14259
14260     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14261
14262     if(this.closable !== false){
14263         this.el.addClass("x-dlg-closable");
14264         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14265         this.close.on("click", this.closeClick, this);
14266         this.close.addClassOnOver("x-dlg-close-over");
14267     }
14268     if(this.collapsible !== false){
14269         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14270         this.collapseBtn.on("click", this.collapseClick, this);
14271         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14272         this.header.on("dblclick", this.collapseClick, this);
14273     }
14274     if(this.resizable !== false){
14275         this.el.addClass("x-dlg-resizable");
14276         this.resizer = new Roo.Resizable(el, {
14277             minWidth: this.minWidth || 80,
14278             minHeight:this.minHeight || 80,
14279             handles: this.resizeHandles || "all",
14280             pinned: true
14281         });
14282         this.resizer.on("beforeresize", this.beforeResize, this);
14283         this.resizer.on("resize", this.onResize, this);
14284     }
14285     if(this.draggable !== false){
14286         el.addClass("x-dlg-draggable");
14287         if (!this.proxyDrag) {
14288             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14289         }
14290         else {
14291             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14292         }
14293         dd.setHandleElId(this.header.id);
14294         dd.endDrag = this.endMove.createDelegate(this);
14295         dd.startDrag = this.startMove.createDelegate(this);
14296         dd.onDrag = this.onDrag.createDelegate(this);
14297         dd.scroll = false;
14298         this.dd = dd;
14299     }
14300     if(this.modal){
14301         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14302         this.mask.enableDisplayMode("block");
14303         this.mask.hide();
14304         this.el.addClass("x-dlg-modal");
14305     }
14306     if(this.shadow){
14307         this.shadow = new Roo.Shadow({
14308             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14309             offset : this.shadowOffset
14310         });
14311     }else{
14312         this.shadowOffset = 0;
14313     }
14314     if(Roo.useShims && this.shim !== false){
14315         this.shim = this.el.createShim();
14316         this.shim.hide = this.hideAction;
14317         this.shim.hide();
14318     }else{
14319         this.shim = false;
14320     }
14321     if(this.autoTabs){
14322         this.initTabs();
14323     }
14324     if (this.buttons) { 
14325         var bts= this.buttons;
14326         this.buttons = [];
14327         Roo.each(bts, function(b) {
14328             this.addButton(b);
14329         }, this);
14330     }
14331     
14332     
14333     this.addEvents({
14334         /**
14335          * @event keydown
14336          * Fires when a key is pressed
14337          * @param {Roo.BasicDialog} this
14338          * @param {Roo.EventObject} e
14339          */
14340         "keydown" : true,
14341         /**
14342          * @event move
14343          * Fires when this dialog is moved by the user.
14344          * @param {Roo.BasicDialog} this
14345          * @param {Number} x The new page X
14346          * @param {Number} y The new page Y
14347          */
14348         "move" : true,
14349         /**
14350          * @event resize
14351          * Fires when this dialog is resized by the user.
14352          * @param {Roo.BasicDialog} this
14353          * @param {Number} width The new width
14354          * @param {Number} height The new height
14355          */
14356         "resize" : true,
14357         /**
14358          * @event beforehide
14359          * Fires before this dialog is hidden.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "beforehide" : true,
14363         /**
14364          * @event hide
14365          * Fires when this dialog is hidden.
14366          * @param {Roo.BasicDialog} this
14367          */
14368         "hide" : true,
14369         /**
14370          * @event beforeshow
14371          * Fires before this dialog is shown.
14372          * @param {Roo.BasicDialog} this
14373          */
14374         "beforeshow" : true,
14375         /**
14376          * @event show
14377          * Fires when this dialog is shown.
14378          * @param {Roo.BasicDialog} this
14379          */
14380         "show" : true
14381     });
14382     el.on("keydown", this.onKeyDown, this);
14383     el.on("mousedown", this.toFront, this);
14384     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14385     this.el.hide();
14386     Roo.DialogManager.register(this);
14387     Roo.BasicDialog.superclass.constructor.call(this);
14388 };
14389
14390 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14391     shadowOffset: Roo.isIE ? 6 : 5,
14392     minHeight: 80,
14393     minWidth: 200,
14394     minButtonWidth: 75,
14395     defaultButton: null,
14396     buttonAlign: "right",
14397     tabTag: 'div',
14398     firstShow: true,
14399
14400     /**
14401      * Sets the dialog title text
14402      * @param {String} text The title text to display
14403      * @return {Roo.BasicDialog} this
14404      */
14405     setTitle : function(text){
14406         this.header.update(text);
14407         return this;
14408     },
14409
14410     // private
14411     closeClick : function(){
14412         this.hide();
14413     },
14414
14415     // private
14416     collapseClick : function(){
14417         this[this.collapsed ? "expand" : "collapse"]();
14418     },
14419
14420     /**
14421      * Collapses the dialog to its minimized state (only the title bar is visible).
14422      * Equivalent to the user clicking the collapse dialog button.
14423      */
14424     collapse : function(){
14425         if(!this.collapsed){
14426             this.collapsed = true;
14427             this.el.addClass("x-dlg-collapsed");
14428             this.restoreHeight = this.el.getHeight();
14429             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14430         }
14431     },
14432
14433     /**
14434      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14435      * clicking the expand dialog button.
14436      */
14437     expand : function(){
14438         if(this.collapsed){
14439             this.collapsed = false;
14440             this.el.removeClass("x-dlg-collapsed");
14441             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14442         }
14443     },
14444
14445     /**
14446      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14447      * @return {Roo.TabPanel} The tabs component
14448      */
14449     initTabs : function(){
14450         var tabs = this.getTabs();
14451         while(tabs.getTab(0)){
14452             tabs.removeTab(0);
14453         }
14454         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14455             var dom = el.dom;
14456             tabs.addTab(Roo.id(dom), dom.title);
14457             dom.title = "";
14458         });
14459         tabs.activate(0);
14460         return tabs;
14461     },
14462
14463     // private
14464     beforeResize : function(){
14465         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14466     },
14467
14468     // private
14469     onResize : function(){
14470         this.refreshSize();
14471         this.syncBodyHeight();
14472         this.adjustAssets();
14473         this.focus();
14474         this.fireEvent("resize", this, this.size.width, this.size.height);
14475     },
14476
14477     // private
14478     onKeyDown : function(e){
14479         if(this.isVisible()){
14480             this.fireEvent("keydown", this, e);
14481         }
14482     },
14483
14484     /**
14485      * Resizes the dialog.
14486      * @param {Number} width
14487      * @param {Number} height
14488      * @return {Roo.BasicDialog} this
14489      */
14490     resizeTo : function(width, height){
14491         this.el.setSize(width, height);
14492         this.size = {width: width, height: height};
14493         this.syncBodyHeight();
14494         if(this.fixedcenter){
14495             this.center();
14496         }
14497         if(this.isVisible()){
14498             this.constrainXY();
14499             this.adjustAssets();
14500         }
14501         this.fireEvent("resize", this, width, height);
14502         return this;
14503     },
14504
14505
14506     /**
14507      * Resizes the dialog to fit the specified content size.
14508      * @param {Number} width
14509      * @param {Number} height
14510      * @return {Roo.BasicDialog} this
14511      */
14512     setContentSize : function(w, h){
14513         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14514         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14515         //if(!this.el.isBorderBox()){
14516             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14517             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14518         //}
14519         if(this.tabs){
14520             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14521             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14522         }
14523         this.resizeTo(w, h);
14524         return this;
14525     },
14526
14527     /**
14528      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14529      * executed in response to a particular key being pressed while the dialog is active.
14530      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14531      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14532      * @param {Function} fn The function to call
14533      * @param {Object} scope (optional) The scope of the function
14534      * @return {Roo.BasicDialog} this
14535      */
14536     addKeyListener : function(key, fn, scope){
14537         var keyCode, shift, ctrl, alt;
14538         if(typeof key == "object" && !(key instanceof Array)){
14539             keyCode = key["key"];
14540             shift = key["shift"];
14541             ctrl = key["ctrl"];
14542             alt = key["alt"];
14543         }else{
14544             keyCode = key;
14545         }
14546         var handler = function(dlg, e){
14547             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14548                 var k = e.getKey();
14549                 if(keyCode instanceof Array){
14550                     for(var i = 0, len = keyCode.length; i < len; i++){
14551                         if(keyCode[i] == k){
14552                           fn.call(scope || window, dlg, k, e);
14553                           return;
14554                         }
14555                     }
14556                 }else{
14557                     if(k == keyCode){
14558                         fn.call(scope || window, dlg, k, e);
14559                     }
14560                 }
14561             }
14562         };
14563         this.on("keydown", handler);
14564         return this;
14565     },
14566
14567     /**
14568      * Returns the TabPanel component (creates it if it doesn't exist).
14569      * Note: If you wish to simply check for the existence of tabs without creating them,
14570      * check for a null 'tabs' property.
14571      * @return {Roo.TabPanel} The tabs component
14572      */
14573     getTabs : function(){
14574         if(!this.tabs){
14575             this.el.addClass("x-dlg-auto-tabs");
14576             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14577             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14578         }
14579         return this.tabs;
14580     },
14581
14582     /**
14583      * Adds a button to the footer section of the dialog.
14584      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14585      * object or a valid Roo.DomHelper element config
14586      * @param {Function} handler The function called when the button is clicked
14587      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14588      * @return {Roo.Button} The new button
14589      */
14590     addButton : function(config, handler, scope){
14591         var dh = Roo.DomHelper;
14592         if(!this.footer){
14593             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14594         }
14595         if(!this.btnContainer){
14596             var tb = this.footer.createChild({
14597
14598                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14599                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14600             }, null, true);
14601             this.btnContainer = tb.firstChild.firstChild.firstChild;
14602         }
14603         var bconfig = {
14604             handler: handler,
14605             scope: scope,
14606             minWidth: this.minButtonWidth,
14607             hideParent:true
14608         };
14609         if(typeof config == "string"){
14610             bconfig.text = config;
14611         }else{
14612             if(config.tag){
14613                 bconfig.dhconfig = config;
14614             }else{
14615                 Roo.apply(bconfig, config);
14616             }
14617         }
14618         var fc = false;
14619         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14620             bconfig.position = Math.max(0, bconfig.position);
14621             fc = this.btnContainer.childNodes[bconfig.position];
14622         }
14623          
14624         var btn = new Roo.Button(
14625             fc ? 
14626                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14627                 : this.btnContainer.appendChild(document.createElement("td")),
14628             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14629             bconfig
14630         );
14631         this.syncBodyHeight();
14632         if(!this.buttons){
14633             /**
14634              * Array of all the buttons that have been added to this dialog via addButton
14635              * @type Array
14636              */
14637             this.buttons = [];
14638         }
14639         this.buttons.push(btn);
14640         return btn;
14641     },
14642
14643     /**
14644      * Sets the default button to be focused when the dialog is displayed.
14645      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14646      * @return {Roo.BasicDialog} this
14647      */
14648     setDefaultButton : function(btn){
14649         this.defaultButton = btn;
14650         return this;
14651     },
14652
14653     // private
14654     getHeaderFooterHeight : function(safe){
14655         var height = 0;
14656         if(this.header){
14657            height += this.header.getHeight();
14658         }
14659         if(this.footer){
14660            var fm = this.footer.getMargins();
14661             height += (this.footer.getHeight()+fm.top+fm.bottom);
14662         }
14663         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14664         height += this.centerBg.getPadding("tb");
14665         return height;
14666     },
14667
14668     // private
14669     syncBodyHeight : function(){
14670         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14671         var height = this.size.height - this.getHeaderFooterHeight(false);
14672         bd.setHeight(height-bd.getMargins("tb"));
14673         var hh = this.header.getHeight();
14674         var h = this.size.height-hh;
14675         cb.setHeight(h);
14676         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14677         bw.setHeight(h-cb.getPadding("tb"));
14678         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14679         bd.setWidth(bw.getWidth(true));
14680         if(this.tabs){
14681             this.tabs.syncHeight();
14682             if(Roo.isIE){
14683                 this.tabs.el.repaint();
14684             }
14685         }
14686     },
14687
14688     /**
14689      * Restores the previous state of the dialog if Roo.state is configured.
14690      * @return {Roo.BasicDialog} this
14691      */
14692     restoreState : function(){
14693         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14694         if(box && box.width){
14695             this.xy = [box.x, box.y];
14696             this.resizeTo(box.width, box.height);
14697         }
14698         return this;
14699     },
14700
14701     // private
14702     beforeShow : function(){
14703         this.expand();
14704         if(this.fixedcenter){
14705             this.xy = this.el.getCenterXY(true);
14706         }
14707         if(this.modal){
14708             Roo.get(document.body).addClass("x-body-masked");
14709             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14710             this.mask.show();
14711         }
14712         this.constrainXY();
14713     },
14714
14715     // private
14716     animShow : function(){
14717         var b = Roo.get(this.animateTarget).getBox();
14718         this.proxy.setSize(b.width, b.height);
14719         this.proxy.setLocation(b.x, b.y);
14720         this.proxy.show();
14721         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14722                     true, .35, this.showEl.createDelegate(this));
14723     },
14724
14725     /**
14726      * Shows the dialog.
14727      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14728      * @return {Roo.BasicDialog} this
14729      */
14730     show : function(animateTarget){
14731         if (this.fireEvent("beforeshow", this) === false){
14732             return;
14733         }
14734         if(this.syncHeightBeforeShow){
14735             this.syncBodyHeight();
14736         }else if(this.firstShow){
14737             this.firstShow = false;
14738             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14739         }
14740         this.animateTarget = animateTarget || this.animateTarget;
14741         if(!this.el.isVisible()){
14742             this.beforeShow();
14743             if(this.animateTarget && Roo.get(this.animateTarget)){
14744                 this.animShow();
14745             }else{
14746                 this.showEl();
14747             }
14748         }
14749         return this;
14750     },
14751
14752     // private
14753     showEl : function(){
14754         this.proxy.hide();
14755         this.el.setXY(this.xy);
14756         this.el.show();
14757         this.adjustAssets(true);
14758         this.toFront();
14759         this.focus();
14760         // IE peekaboo bug - fix found by Dave Fenwick
14761         if(Roo.isIE){
14762             this.el.repaint();
14763         }
14764         this.fireEvent("show", this);
14765     },
14766
14767     /**
14768      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14769      * dialog itself will receive focus.
14770      */
14771     focus : function(){
14772         if(this.defaultButton){
14773             this.defaultButton.focus();
14774         }else{
14775             this.focusEl.focus();
14776         }
14777     },
14778
14779     // private
14780     constrainXY : function(){
14781         if(this.constraintoviewport !== false){
14782             if(!this.viewSize){
14783                 if(this.container){
14784                     var s = this.container.getSize();
14785                     this.viewSize = [s.width, s.height];
14786                 }else{
14787                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14788                 }
14789             }
14790             var s = Roo.get(this.container||document).getScroll();
14791
14792             var x = this.xy[0], y = this.xy[1];
14793             var w = this.size.width, h = this.size.height;
14794             var vw = this.viewSize[0], vh = this.viewSize[1];
14795             // only move it if it needs it
14796             var moved = false;
14797             // first validate right/bottom
14798             if(x + w > vw+s.left){
14799                 x = vw - w;
14800                 moved = true;
14801             }
14802             if(y + h > vh+s.top){
14803                 y = vh - h;
14804                 moved = true;
14805             }
14806             // then make sure top/left isn't negative
14807             if(x < s.left){
14808                 x = s.left;
14809                 moved = true;
14810             }
14811             if(y < s.top){
14812                 y = s.top;
14813                 moved = true;
14814             }
14815             if(moved){
14816                 // cache xy
14817                 this.xy = [x, y];
14818                 if(this.isVisible()){
14819                     this.el.setLocation(x, y);
14820                     this.adjustAssets();
14821                 }
14822             }
14823         }
14824     },
14825
14826     // private
14827     onDrag : function(){
14828         if(!this.proxyDrag){
14829             this.xy = this.el.getXY();
14830             this.adjustAssets();
14831         }
14832     },
14833
14834     // private
14835     adjustAssets : function(doShow){
14836         var x = this.xy[0], y = this.xy[1];
14837         var w = this.size.width, h = this.size.height;
14838         if(doShow === true){
14839             if(this.shadow){
14840                 this.shadow.show(this.el);
14841             }
14842             if(this.shim){
14843                 this.shim.show();
14844             }
14845         }
14846         if(this.shadow && this.shadow.isVisible()){
14847             this.shadow.show(this.el);
14848         }
14849         if(this.shim && this.shim.isVisible()){
14850             this.shim.setBounds(x, y, w, h);
14851         }
14852     },
14853
14854     // private
14855     adjustViewport : function(w, h){
14856         if(!w || !h){
14857             w = Roo.lib.Dom.getViewWidth();
14858             h = Roo.lib.Dom.getViewHeight();
14859         }
14860         // cache the size
14861         this.viewSize = [w, h];
14862         if(this.modal && this.mask.isVisible()){
14863             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14864             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14865         }
14866         if(this.isVisible()){
14867             this.constrainXY();
14868         }
14869     },
14870
14871     /**
14872      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14873      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14874      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14875      */
14876     destroy : function(removeEl){
14877         if(this.isVisible()){
14878             this.animateTarget = null;
14879             this.hide();
14880         }
14881         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14882         if(this.tabs){
14883             this.tabs.destroy(removeEl);
14884         }
14885         Roo.destroy(
14886              this.shim,
14887              this.proxy,
14888              this.resizer,
14889              this.close,
14890              this.mask
14891         );
14892         if(this.dd){
14893             this.dd.unreg();
14894         }
14895         if(this.buttons){
14896            for(var i = 0, len = this.buttons.length; i < len; i++){
14897                this.buttons[i].destroy();
14898            }
14899         }
14900         this.el.removeAllListeners();
14901         if(removeEl === true){
14902             this.el.update("");
14903             this.el.remove();
14904         }
14905         Roo.DialogManager.unregister(this);
14906     },
14907
14908     // private
14909     startMove : function(){
14910         if(this.proxyDrag){
14911             this.proxy.show();
14912         }
14913         if(this.constraintoviewport !== false){
14914             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14915         }
14916     },
14917
14918     // private
14919     endMove : function(){
14920         if(!this.proxyDrag){
14921             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14922         }else{
14923             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14924             this.proxy.hide();
14925         }
14926         this.refreshSize();
14927         this.adjustAssets();
14928         this.focus();
14929         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14930     },
14931
14932     /**
14933      * Brings this dialog to the front of any other visible dialogs
14934      * @return {Roo.BasicDialog} this
14935      */
14936     toFront : function(){
14937         Roo.DialogManager.bringToFront(this);
14938         return this;
14939     },
14940
14941     /**
14942      * Sends this dialog to the back (under) of any other visible dialogs
14943      * @return {Roo.BasicDialog} this
14944      */
14945     toBack : function(){
14946         Roo.DialogManager.sendToBack(this);
14947         return this;
14948     },
14949
14950     /**
14951      * Centers this dialog in the viewport
14952      * @return {Roo.BasicDialog} this
14953      */
14954     center : function(){
14955         var xy = this.el.getCenterXY(true);
14956         this.moveTo(xy[0], xy[1]);
14957         return this;
14958     },
14959
14960     /**
14961      * Moves the dialog's top-left corner to the specified point
14962      * @param {Number} x
14963      * @param {Number} y
14964      * @return {Roo.BasicDialog} this
14965      */
14966     moveTo : function(x, y){
14967         this.xy = [x,y];
14968         if(this.isVisible()){
14969             this.el.setXY(this.xy);
14970             this.adjustAssets();
14971         }
14972         return this;
14973     },
14974
14975     /**
14976      * Aligns the dialog to the specified element
14977      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14978      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14979      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14980      * @return {Roo.BasicDialog} this
14981      */
14982     alignTo : function(element, position, offsets){
14983         this.xy = this.el.getAlignToXY(element, position, offsets);
14984         if(this.isVisible()){
14985             this.el.setXY(this.xy);
14986             this.adjustAssets();
14987         }
14988         return this;
14989     },
14990
14991     /**
14992      * Anchors an element to another element and realigns it when the window is resized.
14993      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14994      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14995      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14996      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14997      * is a number, it is used as the buffer delay (defaults to 50ms).
14998      * @return {Roo.BasicDialog} this
14999      */
15000     anchorTo : function(el, alignment, offsets, monitorScroll){
15001         var action = function(){
15002             this.alignTo(el, alignment, offsets);
15003         };
15004         Roo.EventManager.onWindowResize(action, this);
15005         var tm = typeof monitorScroll;
15006         if(tm != 'undefined'){
15007             Roo.EventManager.on(window, 'scroll', action, this,
15008                 {buffer: tm == 'number' ? monitorScroll : 50});
15009         }
15010         action.call(this);
15011         return this;
15012     },
15013
15014     /**
15015      * Returns true if the dialog is visible
15016      * @return {Boolean}
15017      */
15018     isVisible : function(){
15019         return this.el.isVisible();
15020     },
15021
15022     // private
15023     animHide : function(callback){
15024         var b = Roo.get(this.animateTarget).getBox();
15025         this.proxy.show();
15026         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15027         this.el.hide();
15028         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15029                     this.hideEl.createDelegate(this, [callback]));
15030     },
15031
15032     /**
15033      * Hides the dialog.
15034      * @param {Function} callback (optional) Function to call when the dialog is hidden
15035      * @return {Roo.BasicDialog} this
15036      */
15037     hide : function(callback){
15038         if (this.fireEvent("beforehide", this) === false){
15039             return;
15040         }
15041         if(this.shadow){
15042             this.shadow.hide();
15043         }
15044         if(this.shim) {
15045           this.shim.hide();
15046         }
15047         // sometimes animateTarget seems to get set.. causing problems...
15048         // this just double checks..
15049         if(this.animateTarget && Roo.get(this.animateTarget)) {
15050            this.animHide(callback);
15051         }else{
15052             this.el.hide();
15053             this.hideEl(callback);
15054         }
15055         return this;
15056     },
15057
15058     // private
15059     hideEl : function(callback){
15060         this.proxy.hide();
15061         if(this.modal){
15062             this.mask.hide();
15063             Roo.get(document.body).removeClass("x-body-masked");
15064         }
15065         this.fireEvent("hide", this);
15066         if(typeof callback == "function"){
15067             callback();
15068         }
15069     },
15070
15071     // private
15072     hideAction : function(){
15073         this.setLeft("-10000px");
15074         this.setTop("-10000px");
15075         this.setStyle("visibility", "hidden");
15076     },
15077
15078     // private
15079     refreshSize : function(){
15080         this.size = this.el.getSize();
15081         this.xy = this.el.getXY();
15082         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15083     },
15084
15085     // private
15086     // z-index is managed by the DialogManager and may be overwritten at any time
15087     setZIndex : function(index){
15088         if(this.modal){
15089             this.mask.setStyle("z-index", index);
15090         }
15091         if(this.shim){
15092             this.shim.setStyle("z-index", ++index);
15093         }
15094         if(this.shadow){
15095             this.shadow.setZIndex(++index);
15096         }
15097         this.el.setStyle("z-index", ++index);
15098         if(this.proxy){
15099             this.proxy.setStyle("z-index", ++index);
15100         }
15101         if(this.resizer){
15102             this.resizer.proxy.setStyle("z-index", ++index);
15103         }
15104
15105         this.lastZIndex = index;
15106     },
15107
15108     /**
15109      * Returns the element for this dialog
15110      * @return {Roo.Element} The underlying dialog Element
15111      */
15112     getEl : function(){
15113         return this.el;
15114     }
15115 });
15116
15117 /**
15118  * @class Roo.DialogManager
15119  * Provides global access to BasicDialogs that have been created and
15120  * support for z-indexing (layering) multiple open dialogs.
15121  */
15122 Roo.DialogManager = function(){
15123     var list = {};
15124     var accessList = [];
15125     var front = null;
15126
15127     // private
15128     var sortDialogs = function(d1, d2){
15129         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15130     };
15131
15132     // private
15133     var orderDialogs = function(){
15134         accessList.sort(sortDialogs);
15135         var seed = Roo.DialogManager.zseed;
15136         for(var i = 0, len = accessList.length; i < len; i++){
15137             var dlg = accessList[i];
15138             if(dlg){
15139                 dlg.setZIndex(seed + (i*10));
15140             }
15141         }
15142     };
15143
15144     return {
15145         /**
15146          * The starting z-index for BasicDialogs (defaults to 9000)
15147          * @type Number The z-index value
15148          */
15149         zseed : 9000,
15150
15151         // private
15152         register : function(dlg){
15153             list[dlg.id] = dlg;
15154             accessList.push(dlg);
15155         },
15156
15157         // private
15158         unregister : function(dlg){
15159             delete list[dlg.id];
15160             var i=0;
15161             var len=0;
15162             if(!accessList.indexOf){
15163                 for(  i = 0, len = accessList.length; i < len; i++){
15164                     if(accessList[i] == dlg){
15165                         accessList.splice(i, 1);
15166                         return;
15167                     }
15168                 }
15169             }else{
15170                  i = accessList.indexOf(dlg);
15171                 if(i != -1){
15172                     accessList.splice(i, 1);
15173                 }
15174             }
15175         },
15176
15177         /**
15178          * Gets a registered dialog by id
15179          * @param {String/Object} id The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         get : function(id){
15183             return typeof id == "object" ? id : list[id];
15184         },
15185
15186         /**
15187          * Brings the specified dialog to the front
15188          * @param {String/Object} dlg The id of the dialog or a dialog
15189          * @return {Roo.BasicDialog} this
15190          */
15191         bringToFront : function(dlg){
15192             dlg = this.get(dlg);
15193             if(dlg != front){
15194                 front = dlg;
15195                 dlg._lastAccess = new Date().getTime();
15196                 orderDialogs();
15197             }
15198             return dlg;
15199         },
15200
15201         /**
15202          * Sends the specified dialog to the back
15203          * @param {String/Object} dlg The id of the dialog or a dialog
15204          * @return {Roo.BasicDialog} this
15205          */
15206         sendToBack : function(dlg){
15207             dlg = this.get(dlg);
15208             dlg._lastAccess = -(new Date().getTime());
15209             orderDialogs();
15210             return dlg;
15211         },
15212
15213         /**
15214          * Hides all dialogs
15215          */
15216         hideAll : function(){
15217             for(var id in list){
15218                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15219                     list[id].hide();
15220                 }
15221             }
15222         }
15223     };
15224 }();
15225
15226 /**
15227  * @class Roo.LayoutDialog
15228  * @extends Roo.BasicDialog
15229  * Dialog which provides adjustments for working with a layout in a Dialog.
15230  * Add your necessary layout config options to the dialog's config.<br>
15231  * Example usage (including a nested layout):
15232  * <pre><code>
15233 if(!dialog){
15234     dialog = new Roo.LayoutDialog("download-dlg", {
15235         modal: true,
15236         width:600,
15237         height:450,
15238         shadow:true,
15239         minWidth:500,
15240         minHeight:350,
15241         autoTabs:true,
15242         proxyDrag:true,
15243         // layout config merges with the dialog config
15244         center:{
15245             tabPosition: "top",
15246             alwaysShowTabs: true
15247         }
15248     });
15249     dialog.addKeyListener(27, dialog.hide, dialog);
15250     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15251     dialog.addButton("Build It!", this.getDownload, this);
15252
15253     // we can even add nested layouts
15254     var innerLayout = new Roo.BorderLayout("dl-inner", {
15255         east: {
15256             initialSize: 200,
15257             autoScroll:true,
15258             split:true
15259         },
15260         center: {
15261             autoScroll:true
15262         }
15263     });
15264     innerLayout.beginUpdate();
15265     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15266     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15267     innerLayout.endUpdate(true);
15268
15269     var layout = dialog.getLayout();
15270     layout.beginUpdate();
15271     layout.add("center", new Roo.ContentPanel("standard-panel",
15272                         {title: "Download the Source", fitToFrame:true}));
15273     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15274                {title: "Build your own roo.js"}));
15275     layout.getRegion("center").showPanel(sp);
15276     layout.endUpdate();
15277 }
15278 </code></pre>
15279     * @constructor
15280     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15281     * @param {Object} config configuration options
15282   */
15283 Roo.LayoutDialog = function(el, cfg){
15284     
15285     var config=  cfg;
15286     if (typeof(cfg) == 'undefined') {
15287         config = Roo.apply({}, el);
15288         // not sure why we use documentElement here.. - it should always be body.
15289         // IE7 borks horribly if we use documentElement.
15290         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15291         //config.autoCreate = true;
15292     }
15293     
15294     
15295     config.autoTabs = false;
15296     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15297     this.body.setStyle({overflow:"hidden", position:"relative"});
15298     this.layout = new Roo.BorderLayout(this.body.dom, config);
15299     this.layout.monitorWindowResize = false;
15300     this.el.addClass("x-dlg-auto-layout");
15301     // fix case when center region overwrites center function
15302     this.center = Roo.BasicDialog.prototype.center;
15303     this.on("show", this.layout.layout, this.layout, true);
15304     if (config.items) {
15305         var xitems = config.items;
15306         delete config.items;
15307         Roo.each(xitems, this.addxtype, this);
15308     }
15309     
15310     
15311 };
15312 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15313     /**
15314      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15315      * @deprecated
15316      */
15317     endUpdate : function(){
15318         this.layout.endUpdate();
15319     },
15320
15321     /**
15322      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15323      *  @deprecated
15324      */
15325     beginUpdate : function(){
15326         this.layout.beginUpdate();
15327     },
15328
15329     /**
15330      * Get the BorderLayout for this dialog
15331      * @return {Roo.BorderLayout}
15332      */
15333     getLayout : function(){
15334         return this.layout;
15335     },
15336
15337     showEl : function(){
15338         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15339         if(Roo.isIE7){
15340             this.layout.layout();
15341         }
15342     },
15343
15344     // private
15345     // Use the syncHeightBeforeShow config option to control this automatically
15346     syncBodyHeight : function(){
15347         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15348         if(this.layout){this.layout.layout();}
15349     },
15350     
15351       /**
15352      * Add an xtype element (actually adds to the layout.)
15353      * @return {Object} xdata xtype object data.
15354      */
15355     
15356     addxtype : function(c) {
15357         return this.layout.addxtype(c);
15358     }
15359 });/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369  
15370 /**
15371  * @class Roo.MessageBox
15372  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15373  * Example usage:
15374  *<pre><code>
15375 // Basic alert:
15376 Roo.Msg.alert('Status', 'Changes saved successfully.');
15377
15378 // Prompt for user data:
15379 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15380     if (btn == 'ok'){
15381         // process text value...
15382     }
15383 });
15384
15385 // Show a dialog using config options:
15386 Roo.Msg.show({
15387    title:'Save Changes?',
15388    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15389    buttons: Roo.Msg.YESNOCANCEL,
15390    fn: processResult,
15391    animEl: 'elId'
15392 });
15393 </code></pre>
15394  * @singleton
15395  */
15396 Roo.MessageBox = function(){
15397     var dlg, opt, mask, waitTimer;
15398     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15399     var buttons, activeTextEl, bwidth;
15400
15401     // private
15402     var handleButton = function(button){
15403         dlg.hide();
15404         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15405     };
15406
15407     // private
15408     var handleHide = function(){
15409         if(opt && opt.cls){
15410             dlg.el.removeClass(opt.cls);
15411         }
15412         if(waitTimer){
15413             Roo.TaskMgr.stop(waitTimer);
15414             waitTimer = null;
15415         }
15416     };
15417
15418     // private
15419     var updateButtons = function(b){
15420         var width = 0;
15421         if(!b){
15422             buttons["ok"].hide();
15423             buttons["cancel"].hide();
15424             buttons["yes"].hide();
15425             buttons["no"].hide();
15426             dlg.footer.dom.style.display = 'none';
15427             return width;
15428         }
15429         dlg.footer.dom.style.display = '';
15430         for(var k in buttons){
15431             if(typeof buttons[k] != "function"){
15432                 if(b[k]){
15433                     buttons[k].show();
15434                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15435                     width += buttons[k].el.getWidth()+15;
15436                 }else{
15437                     buttons[k].hide();
15438                 }
15439             }
15440         }
15441         return width;
15442     };
15443
15444     // private
15445     var handleEsc = function(d, k, e){
15446         if(opt && opt.closable !== false){
15447             dlg.hide();
15448         }
15449         if(e){
15450             e.stopEvent();
15451         }
15452     };
15453
15454     return {
15455         /**
15456          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15457          * @return {Roo.BasicDialog} The BasicDialog element
15458          */
15459         getDialog : function(){
15460            if(!dlg){
15461                 dlg = new Roo.BasicDialog("x-msg-box", {
15462                     autoCreate : true,
15463                     shadow: true,
15464                     draggable: true,
15465                     resizable:false,
15466                     constraintoviewport:false,
15467                     fixedcenter:true,
15468                     collapsible : false,
15469                     shim:true,
15470                     modal: true,
15471                     width:400, height:100,
15472                     buttonAlign:"center",
15473                     closeClick : function(){
15474                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15475                             handleButton("no");
15476                         }else{
15477                             handleButton("cancel");
15478                         }
15479                     }
15480                 });
15481                 dlg.on("hide", handleHide);
15482                 mask = dlg.mask;
15483                 dlg.addKeyListener(27, handleEsc);
15484                 buttons = {};
15485                 var bt = this.buttonText;
15486                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15487                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15488                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15489                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15490                 bodyEl = dlg.body.createChild({
15491
15492                     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>'
15493                 });
15494                 msgEl = bodyEl.dom.firstChild;
15495                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15496                 textboxEl.enableDisplayMode();
15497                 textboxEl.addKeyListener([10,13], function(){
15498                     if(dlg.isVisible() && opt && opt.buttons){
15499                         if(opt.buttons.ok){
15500                             handleButton("ok");
15501                         }else if(opt.buttons.yes){
15502                             handleButton("yes");
15503                         }
15504                     }
15505                 });
15506                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15507                 textareaEl.enableDisplayMode();
15508                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15509                 progressEl.enableDisplayMode();
15510                 var pf = progressEl.dom.firstChild;
15511                 if (pf) {
15512                     pp = Roo.get(pf.firstChild);
15513                     pp.setHeight(pf.offsetHeight);
15514                 }
15515                 
15516             }
15517             return dlg;
15518         },
15519
15520         /**
15521          * Updates the message box body text
15522          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15523          * the XHTML-compliant non-breaking space character '&amp;#160;')
15524          * @return {Roo.MessageBox} This message box
15525          */
15526         updateText : function(text){
15527             if(!dlg.isVisible() && !opt.width){
15528                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15529             }
15530             msgEl.innerHTML = text || '&#160;';
15531             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15532                         Math.max(opt.minWidth || this.minWidth, bwidth));
15533             if(opt.prompt){
15534                 activeTextEl.setWidth(w);
15535             }
15536             if(dlg.isVisible()){
15537                 dlg.fixedcenter = false;
15538             }
15539             dlg.setContentSize(w, bodyEl.getHeight());
15540             if(dlg.isVisible()){
15541                 dlg.fixedcenter = true;
15542             }
15543             return this;
15544         },
15545
15546         /**
15547          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15548          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15549          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15550          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15551          * @return {Roo.MessageBox} This message box
15552          */
15553         updateProgress : function(value, text){
15554             if(text){
15555                 this.updateText(text);
15556             }
15557             if (pp) { // weird bug on my firefox - for some reason this is not defined
15558                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15559             }
15560             return this;
15561         },        
15562
15563         /**
15564          * Returns true if the message box is currently displayed
15565          * @return {Boolean} True if the message box is visible, else false
15566          */
15567         isVisible : function(){
15568             return dlg && dlg.isVisible();  
15569         },
15570
15571         /**
15572          * Hides the message box if it is displayed
15573          */
15574         hide : function(){
15575             if(this.isVisible()){
15576                 dlg.hide();
15577             }  
15578         },
15579
15580         /**
15581          * Displays a new message box, or reinitializes an existing message box, based on the config options
15582          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15583          * The following config object properties are supported:
15584          * <pre>
15585 Property    Type             Description
15586 ----------  ---------------  ------------------------------------------------------------------------------------
15587 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15588                                    closes (defaults to undefined)
15589 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15590                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15591 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15592                                    progress and wait dialogs will ignore this property and always hide the
15593                                    close button as they can only be closed programmatically.
15594 cls               String           A custom CSS class to apply to the message box element
15595 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15596                                    displayed (defaults to 75)
15597 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15598                                    function will be btn (the name of the button that was clicked, if applicable,
15599                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15600                                    Progress and wait dialogs will ignore this option since they do not respond to
15601                                    user actions and can only be closed programmatically, so any required function
15602                                    should be called by the same code after it closes the dialog.
15603 icon              String           A CSS class that provides a background image to be used as an icon for
15604                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15605 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15606 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15607 modal             Boolean          False to allow user interaction with the page while the message box is
15608                                    displayed (defaults to true)
15609 msg               String           A string that will replace the existing message box body text (defaults
15610                                    to the XHTML-compliant non-breaking space character '&#160;')
15611 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15612 progress          Boolean          True to display a progress bar (defaults to false)
15613 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15614 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15615 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15616 title             String           The title text
15617 value             String           The string value to set into the active textbox element if displayed
15618 wait              Boolean          True to display a progress bar (defaults to false)
15619 width             Number           The width of the dialog in pixels
15620 </pre>
15621          *
15622          * Example usage:
15623          * <pre><code>
15624 Roo.Msg.show({
15625    title: 'Address',
15626    msg: 'Please enter your address:',
15627    width: 300,
15628    buttons: Roo.MessageBox.OKCANCEL,
15629    multiline: true,
15630    fn: saveAddress,
15631    animEl: 'addAddressBtn'
15632 });
15633 </code></pre>
15634          * @param {Object} config Configuration options
15635          * @return {Roo.MessageBox} This message box
15636          */
15637         show : function(options){
15638             if(this.isVisible()){
15639                 this.hide();
15640             }
15641             var d = this.getDialog();
15642             opt = options;
15643             d.setTitle(opt.title || "&#160;");
15644             d.close.setDisplayed(opt.closable !== false);
15645             activeTextEl = textboxEl;
15646             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15647             if(opt.prompt){
15648                 if(opt.multiline){
15649                     textboxEl.hide();
15650                     textareaEl.show();
15651                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15652                         opt.multiline : this.defaultTextHeight);
15653                     activeTextEl = textareaEl;
15654                 }else{
15655                     textboxEl.show();
15656                     textareaEl.hide();
15657                 }
15658             }else{
15659                 textboxEl.hide();
15660                 textareaEl.hide();
15661             }
15662             progressEl.setDisplayed(opt.progress === true);
15663             this.updateProgress(0);
15664             activeTextEl.dom.value = opt.value || "";
15665             if(opt.prompt){
15666                 dlg.setDefaultButton(activeTextEl);
15667             }else{
15668                 var bs = opt.buttons;
15669                 var db = null;
15670                 if(bs && bs.ok){
15671                     db = buttons["ok"];
15672                 }else if(bs && bs.yes){
15673                     db = buttons["yes"];
15674                 }
15675                 dlg.setDefaultButton(db);
15676             }
15677             bwidth = updateButtons(opt.buttons);
15678             this.updateText(opt.msg);
15679             if(opt.cls){
15680                 d.el.addClass(opt.cls);
15681             }
15682             d.proxyDrag = opt.proxyDrag === true;
15683             d.modal = opt.modal !== false;
15684             d.mask = opt.modal !== false ? mask : false;
15685             if(!d.isVisible()){
15686                 // force it to the end of the z-index stack so it gets a cursor in FF
15687                 document.body.appendChild(dlg.el.dom);
15688                 d.animateTarget = null;
15689                 d.show(options.animEl);
15690             }
15691             return this;
15692         },
15693
15694         /**
15695          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15696          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15697          * and closing the message box when the process is complete.
15698          * @param {String} title The title bar text
15699          * @param {String} msg The message box body text
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         progress : function(title, msg){
15703             this.show({
15704                 title : title,
15705                 msg : msg,
15706                 buttons: false,
15707                 progress:true,
15708                 closable:false,
15709                 minWidth: this.minProgressWidth,
15710                 modal : true
15711             });
15712             return this;
15713         },
15714
15715         /**
15716          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15717          * If a callback function is passed it will be called after the user clicks the button, and the
15718          * id of the button that was clicked will be passed as the only parameter to the callback
15719          * (could also be the top-right close button).
15720          * @param {String} title The title bar text
15721          * @param {String} msg The message box body text
15722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15723          * @param {Object} scope (optional) The scope of the callback function
15724          * @return {Roo.MessageBox} This message box
15725          */
15726         alert : function(title, msg, fn, scope){
15727             this.show({
15728                 title : title,
15729                 msg : msg,
15730                 buttons: this.OK,
15731                 fn: fn,
15732                 scope : scope,
15733                 modal : true
15734             });
15735             return this;
15736         },
15737
15738         /**
15739          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15740          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15741          * You are responsible for closing the message box when the process is complete.
15742          * @param {String} msg The message box body text
15743          * @param {String} title (optional) The title bar text
15744          * @return {Roo.MessageBox} This message box
15745          */
15746         wait : function(msg, title){
15747             this.show({
15748                 title : title,
15749                 msg : msg,
15750                 buttons: false,
15751                 closable:false,
15752                 progress:true,
15753                 modal:true,
15754                 width:300,
15755                 wait:true
15756             });
15757             waitTimer = Roo.TaskMgr.start({
15758                 run: function(i){
15759                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15760                 },
15761                 interval: 1000
15762             });
15763             return this;
15764         },
15765
15766         /**
15767          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15768          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15769          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15770          * @param {String} title The title bar text
15771          * @param {String} msg The message box body text
15772          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15773          * @param {Object} scope (optional) The scope of the callback function
15774          * @return {Roo.MessageBox} This message box
15775          */
15776         confirm : function(title, msg, fn, scope){
15777             this.show({
15778                 title : title,
15779                 msg : msg,
15780                 buttons: this.YESNO,
15781                 fn: fn,
15782                 scope : scope,
15783                 modal : true
15784             });
15785             return this;
15786         },
15787
15788         /**
15789          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15790          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15791          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15792          * (could also be the top-right close button) and the text that was entered will be passed as the two
15793          * parameters to the callback.
15794          * @param {String} title The title bar text
15795          * @param {String} msg The message box body text
15796          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15797          * @param {Object} scope (optional) The scope of the callback function
15798          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15799          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15800          * @return {Roo.MessageBox} This message box
15801          */
15802         prompt : function(title, msg, fn, scope, multiline){
15803             this.show({
15804                 title : title,
15805                 msg : msg,
15806                 buttons: this.OKCANCEL,
15807                 fn: fn,
15808                 minWidth:250,
15809                 scope : scope,
15810                 prompt:true,
15811                 multiline: multiline,
15812                 modal : true
15813             });
15814             return this;
15815         },
15816
15817         /**
15818          * Button config that displays a single OK button
15819          * @type Object
15820          */
15821         OK : {ok:true},
15822         /**
15823          * Button config that displays Yes and No buttons
15824          * @type Object
15825          */
15826         YESNO : {yes:true, no:true},
15827         /**
15828          * Button config that displays OK and Cancel buttons
15829          * @type Object
15830          */
15831         OKCANCEL : {ok:true, cancel:true},
15832         /**
15833          * Button config that displays Yes, No and Cancel buttons
15834          * @type Object
15835          */
15836         YESNOCANCEL : {yes:true, no:true, cancel:true},
15837
15838         /**
15839          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15840          * @type Number
15841          */
15842         defaultTextHeight : 75,
15843         /**
15844          * The maximum width in pixels of the message box (defaults to 600)
15845          * @type Number
15846          */
15847         maxWidth : 600,
15848         /**
15849          * The minimum width in pixels of the message box (defaults to 100)
15850          * @type Number
15851          */
15852         minWidth : 100,
15853         /**
15854          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15855          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15856          * @type Number
15857          */
15858         minProgressWidth : 250,
15859         /**
15860          * An object containing the default button text strings that can be overriden for localized language support.
15861          * Supported properties are: ok, cancel, yes and no.
15862          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15863          * @type Object
15864          */
15865         buttonText : {
15866             ok : "OK",
15867             cancel : "Cancel",
15868             yes : "Yes",
15869             no : "No"
15870         }
15871     };
15872 }();
15873
15874 /**
15875  * Shorthand for {@link Roo.MessageBox}
15876  */
15877 Roo.Msg = Roo.MessageBox;/*
15878  * Based on:
15879  * Ext JS Library 1.1.1
15880  * Copyright(c) 2006-2007, Ext JS, LLC.
15881  *
15882  * Originally Released Under LGPL - original licence link has changed is not relivant.
15883  *
15884  * Fork - LGPL
15885  * <script type="text/javascript">
15886  */
15887 /**
15888  * @class Roo.QuickTips
15889  * Provides attractive and customizable tooltips for any element.
15890  * @singleton
15891  */
15892 Roo.QuickTips = function(){
15893     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15894     var ce, bd, xy, dd;
15895     var visible = false, disabled = true, inited = false;
15896     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15897     
15898     var onOver = function(e){
15899         if(disabled){
15900             return;
15901         }
15902         var t = e.getTarget();
15903         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15904             return;
15905         }
15906         if(ce && t == ce.el){
15907             clearTimeout(hideProc);
15908             return;
15909         }
15910         if(t && tagEls[t.id]){
15911             tagEls[t.id].el = t;
15912             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15913             return;
15914         }
15915         var ttp, et = Roo.fly(t);
15916         var ns = cfg.namespace;
15917         if(tm.interceptTitles && t.title){
15918             ttp = t.title;
15919             t.qtip = ttp;
15920             t.removeAttribute("title");
15921             e.preventDefault();
15922         }else{
15923             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15924         }
15925         if(ttp){
15926             showProc = show.defer(tm.showDelay, tm, [{
15927                 el: t, 
15928                 text: ttp, 
15929                 width: et.getAttributeNS(ns, cfg.width),
15930                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15931                 title: et.getAttributeNS(ns, cfg.title),
15932                     cls: et.getAttributeNS(ns, cfg.cls)
15933             }]);
15934         }
15935     };
15936     
15937     var onOut = function(e){
15938         clearTimeout(showProc);
15939         var t = e.getTarget();
15940         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15941             hideProc = setTimeout(hide, tm.hideDelay);
15942         }
15943     };
15944     
15945     var onMove = function(e){
15946         if(disabled){
15947             return;
15948         }
15949         xy = e.getXY();
15950         xy[1] += 18;
15951         if(tm.trackMouse && ce){
15952             el.setXY(xy);
15953         }
15954     };
15955     
15956     var onDown = function(e){
15957         clearTimeout(showProc);
15958         clearTimeout(hideProc);
15959         if(!e.within(el)){
15960             if(tm.hideOnClick){
15961                 hide();
15962                 tm.disable();
15963                 tm.enable.defer(100, tm);
15964             }
15965         }
15966     };
15967     
15968     var getPad = function(){
15969         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15970     };
15971
15972     var show = function(o){
15973         if(disabled){
15974             return;
15975         }
15976         clearTimeout(dismissProc);
15977         ce = o;
15978         if(removeCls){ // in case manually hidden
15979             el.removeClass(removeCls);
15980             removeCls = null;
15981         }
15982         if(ce.cls){
15983             el.addClass(ce.cls);
15984             removeCls = ce.cls;
15985         }
15986         if(ce.title){
15987             tipTitle.update(ce.title);
15988             tipTitle.show();
15989         }else{
15990             tipTitle.update('');
15991             tipTitle.hide();
15992         }
15993         el.dom.style.width  = tm.maxWidth+'px';
15994         //tipBody.dom.style.width = '';
15995         tipBodyText.update(o.text);
15996         var p = getPad(), w = ce.width;
15997         if(!w){
15998             var td = tipBodyText.dom;
15999             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16000             if(aw > tm.maxWidth){
16001                 w = tm.maxWidth;
16002             }else if(aw < tm.minWidth){
16003                 w = tm.minWidth;
16004             }else{
16005                 w = aw;
16006             }
16007         }
16008         //tipBody.setWidth(w);
16009         el.setWidth(parseInt(w, 10) + p);
16010         if(ce.autoHide === false){
16011             close.setDisplayed(true);
16012             if(dd){
16013                 dd.unlock();
16014             }
16015         }else{
16016             close.setDisplayed(false);
16017             if(dd){
16018                 dd.lock();
16019             }
16020         }
16021         if(xy){
16022             el.avoidY = xy[1]-18;
16023             el.setXY(xy);
16024         }
16025         if(tm.animate){
16026             el.setOpacity(.1);
16027             el.setStyle("visibility", "visible");
16028             el.fadeIn({callback: afterShow});
16029         }else{
16030             afterShow();
16031         }
16032     };
16033     
16034     var afterShow = function(){
16035         if(ce){
16036             el.show();
16037             esc.enable();
16038             if(tm.autoDismiss && ce.autoHide !== false){
16039                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16040             }
16041         }
16042     };
16043     
16044     var hide = function(noanim){
16045         clearTimeout(dismissProc);
16046         clearTimeout(hideProc);
16047         ce = null;
16048         if(el.isVisible()){
16049             esc.disable();
16050             if(noanim !== true && tm.animate){
16051                 el.fadeOut({callback: afterHide});
16052             }else{
16053                 afterHide();
16054             } 
16055         }
16056     };
16057     
16058     var afterHide = function(){
16059         el.hide();
16060         if(removeCls){
16061             el.removeClass(removeCls);
16062             removeCls = null;
16063         }
16064     };
16065     
16066     return {
16067         /**
16068         * @cfg {Number} minWidth
16069         * The minimum width of the quick tip (defaults to 40)
16070         */
16071        minWidth : 40,
16072         /**
16073         * @cfg {Number} maxWidth
16074         * The maximum width of the quick tip (defaults to 300)
16075         */
16076        maxWidth : 300,
16077         /**
16078         * @cfg {Boolean} interceptTitles
16079         * True to automatically use the element's DOM title value if available (defaults to false)
16080         */
16081        interceptTitles : false,
16082         /**
16083         * @cfg {Boolean} trackMouse
16084         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16085         */
16086        trackMouse : false,
16087         /**
16088         * @cfg {Boolean} hideOnClick
16089         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16090         */
16091        hideOnClick : true,
16092         /**
16093         * @cfg {Number} showDelay
16094         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16095         */
16096        showDelay : 500,
16097         /**
16098         * @cfg {Number} hideDelay
16099         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16100         */
16101        hideDelay : 200,
16102         /**
16103         * @cfg {Boolean} autoHide
16104         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16105         * Used in conjunction with hideDelay.
16106         */
16107        autoHide : true,
16108         /**
16109         * @cfg {Boolean}
16110         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16111         * (defaults to true).  Used in conjunction with autoDismissDelay.
16112         */
16113        autoDismiss : true,
16114         /**
16115         * @cfg {Number}
16116         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16117         */
16118        autoDismissDelay : 5000,
16119        /**
16120         * @cfg {Boolean} animate
16121         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16122         */
16123        animate : false,
16124
16125        /**
16126         * @cfg {String} title
16127         * Title text to display (defaults to '').  This can be any valid HTML markup.
16128         */
16129         title: '',
16130        /**
16131         * @cfg {String} text
16132         * Body text to display (defaults to '').  This can be any valid HTML markup.
16133         */
16134         text : '',
16135        /**
16136         * @cfg {String} cls
16137         * A CSS class to apply to the base quick tip element (defaults to '').
16138         */
16139         cls : '',
16140        /**
16141         * @cfg {Number} width
16142         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16143         * minWidth or maxWidth.
16144         */
16145         width : null,
16146
16147     /**
16148      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16149      * or display QuickTips in a page.
16150      */
16151        init : function(){
16152           tm = Roo.QuickTips;
16153           cfg = tm.tagConfig;
16154           if(!inited){
16155               if(!Roo.isReady){ // allow calling of init() before onReady
16156                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16157                   return;
16158               }
16159               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16160               el.fxDefaults = {stopFx: true};
16161               // maximum custom styling
16162               //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>');
16163               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>');              
16164               tipTitle = el.child('h3');
16165               tipTitle.enableDisplayMode("block");
16166               tipBody = el.child('div.x-tip-bd');
16167               tipBodyText = el.child('div.x-tip-bd-inner');
16168               //bdLeft = el.child('div.x-tip-bd-left');
16169               //bdRight = el.child('div.x-tip-bd-right');
16170               close = el.child('div.x-tip-close');
16171               close.enableDisplayMode("block");
16172               close.on("click", hide);
16173               var d = Roo.get(document);
16174               d.on("mousedown", onDown);
16175               d.on("mouseover", onOver);
16176               d.on("mouseout", onOut);
16177               d.on("mousemove", onMove);
16178               esc = d.addKeyListener(27, hide);
16179               esc.disable();
16180               if(Roo.dd.DD){
16181                   dd = el.initDD("default", null, {
16182                       onDrag : function(){
16183                           el.sync();  
16184                       }
16185                   });
16186                   dd.setHandleElId(tipTitle.id);
16187                   dd.lock();
16188               }
16189               inited = true;
16190           }
16191           this.enable(); 
16192        },
16193
16194     /**
16195      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16196      * are supported:
16197      * <pre>
16198 Property    Type                   Description
16199 ----------  ---------------------  ------------------------------------------------------------------------
16200 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16201      * </ul>
16202      * @param {Object} config The config object
16203      */
16204        register : function(config){
16205            var cs = config instanceof Array ? config : arguments;
16206            for(var i = 0, len = cs.length; i < len; i++) {
16207                var c = cs[i];
16208                var target = c.target;
16209                if(target){
16210                    if(target instanceof Array){
16211                        for(var j = 0, jlen = target.length; j < jlen; j++){
16212                            tagEls[target[j]] = c;
16213                        }
16214                    }else{
16215                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16216                    }
16217                }
16218            }
16219        },
16220
16221     /**
16222      * Removes this quick tip from its element and destroys it.
16223      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16224      */
16225        unregister : function(el){
16226            delete tagEls[Roo.id(el)];
16227        },
16228
16229     /**
16230      * Enable this quick tip.
16231      */
16232        enable : function(){
16233            if(inited && disabled){
16234                locks.pop();
16235                if(locks.length < 1){
16236                    disabled = false;
16237                }
16238            }
16239        },
16240
16241     /**
16242      * Disable this quick tip.
16243      */
16244        disable : function(){
16245           disabled = true;
16246           clearTimeout(showProc);
16247           clearTimeout(hideProc);
16248           clearTimeout(dismissProc);
16249           if(ce){
16250               hide(true);
16251           }
16252           locks.push(1);
16253        },
16254
16255     /**
16256      * Returns true if the quick tip is enabled, else false.
16257      */
16258        isEnabled : function(){
16259             return !disabled;
16260        },
16261
16262         // private
16263        tagConfig : {
16264            namespace : "ext",
16265            attribute : "qtip",
16266            width : "width",
16267            target : "target",
16268            title : "qtitle",
16269            hide : "hide",
16270            cls : "qclass"
16271        }
16272    };
16273 }();
16274
16275 // backwards compat
16276 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16277  * Based on:
16278  * Ext JS Library 1.1.1
16279  * Copyright(c) 2006-2007, Ext JS, LLC.
16280  *
16281  * Originally Released Under LGPL - original licence link has changed is not relivant.
16282  *
16283  * Fork - LGPL
16284  * <script type="text/javascript">
16285  */
16286  
16287
16288 /**
16289  * @class Roo.tree.TreePanel
16290  * @extends Roo.data.Tree
16291
16292  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16293  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16294  * @cfg {Boolean} enableDD true to enable drag and drop
16295  * @cfg {Boolean} enableDrag true to enable just drag
16296  * @cfg {Boolean} enableDrop true to enable just drop
16297  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16298  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16299  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16300  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16301  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16302  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16303  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16304  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16305  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16306  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16307  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16308  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16309  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16310  * @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>
16311  * @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>
16312  * 
16313  * @constructor
16314  * @param {String/HTMLElement/Element} el The container element
16315  * @param {Object} config
16316  */
16317 Roo.tree.TreePanel = function(el, config){
16318     var root = false;
16319     var loader = false;
16320     if (config.root) {
16321         root = config.root;
16322         delete config.root;
16323     }
16324     if (config.loader) {
16325         loader = config.loader;
16326         delete config.loader;
16327     }
16328     
16329     Roo.apply(this, config);
16330     Roo.tree.TreePanel.superclass.constructor.call(this);
16331     this.el = Roo.get(el);
16332     this.el.addClass('x-tree');
16333     //console.log(root);
16334     if (root) {
16335         this.setRootNode( Roo.factory(root, Roo.tree));
16336     }
16337     if (loader) {
16338         this.loader = Roo.factory(loader, Roo.tree);
16339     }
16340    /**
16341     * Read-only. The id of the container element becomes this TreePanel's id.
16342     */
16343    this.id = this.el.id;
16344    this.addEvents({
16345         /**
16346         * @event beforeload
16347         * Fires before a node is loaded, return false to cancel
16348         * @param {Node} node The node being loaded
16349         */
16350         "beforeload" : true,
16351         /**
16352         * @event load
16353         * Fires when a node is loaded
16354         * @param {Node} node The node that was loaded
16355         */
16356         "load" : true,
16357         /**
16358         * @event textchange
16359         * Fires when the text for a node is changed
16360         * @param {Node} node The node
16361         * @param {String} text The new text
16362         * @param {String} oldText The old text
16363         */
16364         "textchange" : true,
16365         /**
16366         * @event beforeexpand
16367         * Fires before a node is expanded, return false to cancel.
16368         * @param {Node} node The node
16369         * @param {Boolean} deep
16370         * @param {Boolean} anim
16371         */
16372         "beforeexpand" : true,
16373         /**
16374         * @event beforecollapse
16375         * Fires before a node is collapsed, return false to cancel.
16376         * @param {Node} node The node
16377         * @param {Boolean} deep
16378         * @param {Boolean} anim
16379         */
16380         "beforecollapse" : true,
16381         /**
16382         * @event expand
16383         * Fires when a node is expanded
16384         * @param {Node} node The node
16385         */
16386         "expand" : true,
16387         /**
16388         * @event disabledchange
16389         * Fires when the disabled status of a node changes
16390         * @param {Node} node The node
16391         * @param {Boolean} disabled
16392         */
16393         "disabledchange" : true,
16394         /**
16395         * @event collapse
16396         * Fires when a node is collapsed
16397         * @param {Node} node The node
16398         */
16399         "collapse" : true,
16400         /**
16401         * @event beforeclick
16402         * Fires before click processing on a node. Return false to cancel the default action.
16403         * @param {Node} node The node
16404         * @param {Roo.EventObject} e The event object
16405         */
16406         "beforeclick":true,
16407         /**
16408         * @event checkchange
16409         * Fires when a node with a checkbox's checked property changes
16410         * @param {Node} this This node
16411         * @param {Boolean} checked
16412         */
16413         "checkchange":true,
16414         /**
16415         * @event click
16416         * Fires when a node is clicked
16417         * @param {Node} node The node
16418         * @param {Roo.EventObject} e The event object
16419         */
16420         "click":true,
16421         /**
16422         * @event dblclick
16423         * Fires when a node is double clicked
16424         * @param {Node} node The node
16425         * @param {Roo.EventObject} e The event object
16426         */
16427         "dblclick":true,
16428         /**
16429         * @event contextmenu
16430         * Fires when a node is right clicked
16431         * @param {Node} node The node
16432         * @param {Roo.EventObject} e The event object
16433         */
16434         "contextmenu":true,
16435         /**
16436         * @event beforechildrenrendered
16437         * Fires right before the child nodes for a node are rendered
16438         * @param {Node} node The node
16439         */
16440         "beforechildrenrendered":true,
16441        /**
16442              * @event startdrag
16443              * Fires when a node starts being dragged
16444              * @param {Roo.tree.TreePanel} this
16445              * @param {Roo.tree.TreeNode} node
16446              * @param {event} e The raw browser event
16447              */ 
16448             "startdrag" : true,
16449             /**
16450              * @event enddrag
16451              * Fires when a drag operation is complete
16452              * @param {Roo.tree.TreePanel} this
16453              * @param {Roo.tree.TreeNode} node
16454              * @param {event} e The raw browser event
16455              */
16456             "enddrag" : true,
16457             /**
16458              * @event dragdrop
16459              * Fires when a dragged node is dropped on a valid DD target
16460              * @param {Roo.tree.TreePanel} this
16461              * @param {Roo.tree.TreeNode} node
16462              * @param {DD} dd The dd it was dropped on
16463              * @param {event} e The raw browser event
16464              */
16465             "dragdrop" : true,
16466             /**
16467              * @event beforenodedrop
16468              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16469              * passed to handlers has the following properties:<br />
16470              * <ul style="padding:5px;padding-left:16px;">
16471              * <li>tree - The TreePanel</li>
16472              * <li>target - The node being targeted for the drop</li>
16473              * <li>data - The drag data from the drag source</li>
16474              * <li>point - The point of the drop - append, above or below</li>
16475              * <li>source - The drag source</li>
16476              * <li>rawEvent - Raw mouse event</li>
16477              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16478              * to be inserted by setting them on this object.</li>
16479              * <li>cancel - Set this to true to cancel the drop.</li>
16480              * </ul>
16481              * @param {Object} dropEvent
16482              */
16483             "beforenodedrop" : true,
16484             /**
16485              * @event nodedrop
16486              * Fires after a DD object is dropped on a node in this tree. 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 - Dropped node(s).</li>
16496              * </ul>
16497              * @param {Object} dropEvent
16498              */
16499             "nodedrop" : true,
16500              /**
16501              * @event nodedragover
16502              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16503              * passed to handlers has the following properties:<br />
16504              * <ul style="padding:5px;padding-left:16px;">
16505              * <li>tree - The TreePanel</li>
16506              * <li>target - The node being targeted for the drop</li>
16507              * <li>data - The drag data from the drag source</li>
16508              * <li>point - The point of the drop - append, above or below</li>
16509              * <li>source - The drag source</li>
16510              * <li>rawEvent - Raw mouse event</li>
16511              * <li>dropNode - Drop node(s) provided by the source.</li>
16512              * <li>cancel - Set this to true to signal drop not allowed.</li>
16513              * </ul>
16514              * @param {Object} dragOverEvent
16515              */
16516             "nodedragover" : true
16517         
16518    });
16519    if(this.singleExpand){
16520        this.on("beforeexpand", this.restrictExpand, this);
16521    }
16522 };
16523 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16524     rootVisible : true,
16525     animate: Roo.enableFx,
16526     lines : true,
16527     enableDD : false,
16528     hlDrop : Roo.enableFx,
16529   
16530     renderer: false,
16531     
16532     rendererTip: false,
16533     // private
16534     restrictExpand : function(node){
16535         var p = node.parentNode;
16536         if(p){
16537             if(p.expandedChild && p.expandedChild.parentNode == p){
16538                 p.expandedChild.collapse();
16539             }
16540             p.expandedChild = node;
16541         }
16542     },
16543
16544     // private override
16545     setRootNode : function(node){
16546         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16547         if(!this.rootVisible){
16548             node.ui = new Roo.tree.RootTreeNodeUI(node);
16549         }
16550         return node;
16551     },
16552
16553     /**
16554      * Returns the container element for this TreePanel
16555      */
16556     getEl : function(){
16557         return this.el;
16558     },
16559
16560     /**
16561      * Returns the default TreeLoader for this TreePanel
16562      */
16563     getLoader : function(){
16564         return this.loader;
16565     },
16566
16567     /**
16568      * Expand all nodes
16569      */
16570     expandAll : function(){
16571         this.root.expand(true);
16572     },
16573
16574     /**
16575      * Collapse all nodes
16576      */
16577     collapseAll : function(){
16578         this.root.collapse(true);
16579     },
16580
16581     /**
16582      * Returns the selection model used by this TreePanel
16583      */
16584     getSelectionModel : function(){
16585         if(!this.selModel){
16586             this.selModel = new Roo.tree.DefaultSelectionModel();
16587         }
16588         return this.selModel;
16589     },
16590
16591     /**
16592      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16593      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16594      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16595      * @return {Array}
16596      */
16597     getChecked : function(a, startNode){
16598         startNode = startNode || this.root;
16599         var r = [];
16600         var f = function(){
16601             if(this.attributes.checked){
16602                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16603             }
16604         }
16605         startNode.cascade(f);
16606         return r;
16607     },
16608
16609     /**
16610      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16611      * @param {String} path
16612      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16613      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16614      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16615      */
16616     expandPath : function(path, attr, callback){
16617         attr = attr || "id";
16618         var keys = path.split(this.pathSeparator);
16619         var curNode = this.root;
16620         if(curNode.attributes[attr] != keys[1]){ // invalid root
16621             if(callback){
16622                 callback(false, null);
16623             }
16624             return;
16625         }
16626         var index = 1;
16627         var f = function(){
16628             if(++index == keys.length){
16629                 if(callback){
16630                     callback(true, curNode);
16631                 }
16632                 return;
16633             }
16634             var c = curNode.findChild(attr, keys[index]);
16635             if(!c){
16636                 if(callback){
16637                     callback(false, curNode);
16638                 }
16639                 return;
16640             }
16641             curNode = c;
16642             c.expand(false, false, f);
16643         };
16644         curNode.expand(false, false, f);
16645     },
16646
16647     /**
16648      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16649      * @param {String} path
16650      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16651      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16652      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16653      */
16654     selectPath : function(path, attr, callback){
16655         attr = attr || "id";
16656         var keys = path.split(this.pathSeparator);
16657         var v = keys.pop();
16658         if(keys.length > 0){
16659             var f = function(success, node){
16660                 if(success && node){
16661                     var n = node.findChild(attr, v);
16662                     if(n){
16663                         n.select();
16664                         if(callback){
16665                             callback(true, n);
16666                         }
16667                     }else if(callback){
16668                         callback(false, n);
16669                     }
16670                 }else{
16671                     if(callback){
16672                         callback(false, n);
16673                     }
16674                 }
16675             };
16676             this.expandPath(keys.join(this.pathSeparator), attr, f);
16677         }else{
16678             this.root.select();
16679             if(callback){
16680                 callback(true, this.root);
16681             }
16682         }
16683     },
16684
16685     getTreeEl : function(){
16686         return this.el;
16687     },
16688
16689     /**
16690      * Trigger rendering of this TreePanel
16691      */
16692     render : function(){
16693         if (this.innerCt) {
16694             return this; // stop it rendering more than once!!
16695         }
16696         
16697         this.innerCt = this.el.createChild({tag:"ul",
16698                cls:"x-tree-root-ct " +
16699                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16700
16701         if(this.containerScroll){
16702             Roo.dd.ScrollManager.register(this.el);
16703         }
16704         if((this.enableDD || this.enableDrop) && !this.dropZone){
16705            /**
16706             * The dropZone used by this tree if drop is enabled
16707             * @type Roo.tree.TreeDropZone
16708             */
16709              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16710                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16711            });
16712         }
16713         if((this.enableDD || this.enableDrag) && !this.dragZone){
16714            /**
16715             * The dragZone used by this tree if drag is enabled
16716             * @type Roo.tree.TreeDragZone
16717             */
16718             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16719                ddGroup: this.ddGroup || "TreeDD",
16720                scroll: this.ddScroll
16721            });
16722         }
16723         this.getSelectionModel().init(this);
16724         if (!this.root) {
16725             console.log("ROOT not set in tree");
16726             return;
16727         }
16728         this.root.render();
16729         if(!this.rootVisible){
16730             this.root.renderChildren();
16731         }
16732         return this;
16733     }
16734 });/*
16735  * Based on:
16736  * Ext JS Library 1.1.1
16737  * Copyright(c) 2006-2007, Ext JS, LLC.
16738  *
16739  * Originally Released Under LGPL - original licence link has changed is not relivant.
16740  *
16741  * Fork - LGPL
16742  * <script type="text/javascript">
16743  */
16744  
16745
16746 /**
16747  * @class Roo.tree.DefaultSelectionModel
16748  * @extends Roo.util.Observable
16749  * The default single selection for a TreePanel.
16750  */
16751 Roo.tree.DefaultSelectionModel = function(){
16752    this.selNode = null;
16753    
16754    this.addEvents({
16755        /**
16756         * @event selectionchange
16757         * Fires when the selected node changes
16758         * @param {DefaultSelectionModel} this
16759         * @param {TreeNode} node the new selection
16760         */
16761        "selectionchange" : true,
16762
16763        /**
16764         * @event beforeselect
16765         * Fires before the selected node changes, return false to cancel the change
16766         * @param {DefaultSelectionModel} this
16767         * @param {TreeNode} node the new selection
16768         * @param {TreeNode} node the old selection
16769         */
16770        "beforeselect" : true
16771    });
16772 };
16773
16774 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16775     init : function(tree){
16776         this.tree = tree;
16777         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16778         tree.on("click", this.onNodeClick, this);
16779     },
16780     
16781     onNodeClick : function(node, e){
16782         if (e.ctrlKey && this.selNode == node)  {
16783             this.unselect(node);
16784             return;
16785         }
16786         this.select(node);
16787     },
16788     
16789     /**
16790      * Select a node.
16791      * @param {TreeNode} node The node to select
16792      * @return {TreeNode} The selected node
16793      */
16794     select : function(node){
16795         var last = this.selNode;
16796         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16797             if(last){
16798                 last.ui.onSelectedChange(false);
16799             }
16800             this.selNode = node;
16801             node.ui.onSelectedChange(true);
16802             this.fireEvent("selectionchange", this, node, last);
16803         }
16804         return node;
16805     },
16806     
16807     /**
16808      * Deselect a node.
16809      * @param {TreeNode} node The node to unselect
16810      */
16811     unselect : function(node){
16812         if(this.selNode == node){
16813             this.clearSelections();
16814         }    
16815     },
16816     
16817     /**
16818      * Clear all selections
16819      */
16820     clearSelections : function(){
16821         var n = this.selNode;
16822         if(n){
16823             n.ui.onSelectedChange(false);
16824             this.selNode = null;
16825             this.fireEvent("selectionchange", this, null);
16826         }
16827         return n;
16828     },
16829     
16830     /**
16831      * Get the selected node
16832      * @return {TreeNode} The selected node
16833      */
16834     getSelectedNode : function(){
16835         return this.selNode;    
16836     },
16837     
16838     /**
16839      * Returns true if the node is selected
16840      * @param {TreeNode} node The node to check
16841      * @return {Boolean}
16842      */
16843     isSelected : function(node){
16844         return this.selNode == node;  
16845     },
16846
16847     /**
16848      * Selects the node above the selected node in the tree, intelligently walking the nodes
16849      * @return TreeNode The new selection
16850      */
16851     selectPrevious : function(){
16852         var s = this.selNode || this.lastSelNode;
16853         if(!s){
16854             return null;
16855         }
16856         var ps = s.previousSibling;
16857         if(ps){
16858             if(!ps.isExpanded() || ps.childNodes.length < 1){
16859                 return this.select(ps);
16860             } else{
16861                 var lc = ps.lastChild;
16862                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16863                     lc = lc.lastChild;
16864                 }
16865                 return this.select(lc);
16866             }
16867         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16868             return this.select(s.parentNode);
16869         }
16870         return null;
16871     },
16872
16873     /**
16874      * Selects the node above the selected node in the tree, intelligently walking the nodes
16875      * @return TreeNode The new selection
16876      */
16877     selectNext : function(){
16878         var s = this.selNode || this.lastSelNode;
16879         if(!s){
16880             return null;
16881         }
16882         if(s.firstChild && s.isExpanded()){
16883              return this.select(s.firstChild);
16884          }else if(s.nextSibling){
16885              return this.select(s.nextSibling);
16886          }else if(s.parentNode){
16887             var newS = null;
16888             s.parentNode.bubble(function(){
16889                 if(this.nextSibling){
16890                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16891                     return false;
16892                 }
16893             });
16894             return newS;
16895          }
16896         return null;
16897     },
16898
16899     onKeyDown : function(e){
16900         var s = this.selNode || this.lastSelNode;
16901         // undesirable, but required
16902         var sm = this;
16903         if(!s){
16904             return;
16905         }
16906         var k = e.getKey();
16907         switch(k){
16908              case e.DOWN:
16909                  e.stopEvent();
16910                  this.selectNext();
16911              break;
16912              case e.UP:
16913                  e.stopEvent();
16914                  this.selectPrevious();
16915              break;
16916              case e.RIGHT:
16917                  e.preventDefault();
16918                  if(s.hasChildNodes()){
16919                      if(!s.isExpanded()){
16920                          s.expand();
16921                      }else if(s.firstChild){
16922                          this.select(s.firstChild, e);
16923                      }
16924                  }
16925              break;
16926              case e.LEFT:
16927                  e.preventDefault();
16928                  if(s.hasChildNodes() && s.isExpanded()){
16929                      s.collapse();
16930                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16931                      this.select(s.parentNode, e);
16932                  }
16933              break;
16934         };
16935     }
16936 });
16937
16938 /**
16939  * @class Roo.tree.MultiSelectionModel
16940  * @extends Roo.util.Observable
16941  * Multi selection for a TreePanel.
16942  */
16943 Roo.tree.MultiSelectionModel = function(){
16944    this.selNodes = [];
16945    this.selMap = {};
16946    this.addEvents({
16947        /**
16948         * @event selectionchange
16949         * Fires when the selected nodes change
16950         * @param {MultiSelectionModel} this
16951         * @param {Array} nodes Array of the selected nodes
16952         */
16953        "selectionchange" : true
16954    });
16955 };
16956
16957 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16958     init : function(tree){
16959         this.tree = tree;
16960         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16961         tree.on("click", this.onNodeClick, this);
16962     },
16963     
16964     onNodeClick : function(node, e){
16965         this.select(node, e, e.ctrlKey);
16966     },
16967     
16968     /**
16969      * Select a node.
16970      * @param {TreeNode} node The node to select
16971      * @param {EventObject} e (optional) An event associated with the selection
16972      * @param {Boolean} keepExisting True to retain existing selections
16973      * @return {TreeNode} The selected node
16974      */
16975     select : function(node, e, keepExisting){
16976         if(keepExisting !== true){
16977             this.clearSelections(true);
16978         }
16979         if(this.isSelected(node)){
16980             this.lastSelNode = node;
16981             return node;
16982         }
16983         this.selNodes.push(node);
16984         this.selMap[node.id] = node;
16985         this.lastSelNode = node;
16986         node.ui.onSelectedChange(true);
16987         this.fireEvent("selectionchange", this, this.selNodes);
16988         return node;
16989     },
16990     
16991     /**
16992      * Deselect a node.
16993      * @param {TreeNode} node The node to unselect
16994      */
16995     unselect : function(node){
16996         if(this.selMap[node.id]){
16997             node.ui.onSelectedChange(false);
16998             var sn = this.selNodes;
16999             var index = -1;
17000             if(sn.indexOf){
17001                 index = sn.indexOf(node);
17002             }else{
17003                 for(var i = 0, len = sn.length; i < len; i++){
17004                     if(sn[i] == node){
17005                         index = i;
17006                         break;
17007                     }
17008                 }
17009             }
17010             if(index != -1){
17011                 this.selNodes.splice(index, 1);
17012             }
17013             delete this.selMap[node.id];
17014             this.fireEvent("selectionchange", this, this.selNodes);
17015         }
17016     },
17017     
17018     /**
17019      * Clear all selections
17020      */
17021     clearSelections : function(suppressEvent){
17022         var sn = this.selNodes;
17023         if(sn.length > 0){
17024             for(var i = 0, len = sn.length; i < len; i++){
17025                 sn[i].ui.onSelectedChange(false);
17026             }
17027             this.selNodes = [];
17028             this.selMap = {};
17029             if(suppressEvent !== true){
17030                 this.fireEvent("selectionchange", this, this.selNodes);
17031             }
17032         }
17033     },
17034     
17035     /**
17036      * Returns true if the node is selected
17037      * @param {TreeNode} node The node to check
17038      * @return {Boolean}
17039      */
17040     isSelected : function(node){
17041         return this.selMap[node.id] ? true : false;  
17042     },
17043     
17044     /**
17045      * Returns an array of the selected nodes
17046      * @return {Array}
17047      */
17048     getSelectedNodes : function(){
17049         return this.selNodes;    
17050     },
17051
17052     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17053
17054     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17055
17056     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17057 });/*
17058  * Based on:
17059  * Ext JS Library 1.1.1
17060  * Copyright(c) 2006-2007, Ext JS, LLC.
17061  *
17062  * Originally Released Under LGPL - original licence link has changed is not relivant.
17063  *
17064  * Fork - LGPL
17065  * <script type="text/javascript">
17066  */
17067  
17068 /**
17069  * @class Roo.tree.TreeNode
17070  * @extends Roo.data.Node
17071  * @cfg {String} text The text for this node
17072  * @cfg {Boolean} expanded true to start the node expanded
17073  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17074  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17075  * @cfg {Boolean} disabled true to start the node disabled
17076  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17077  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17078  * @cfg {String} cls A css class to be added to the node
17079  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17080  * @cfg {String} href URL of the link used for the node (defaults to #)
17081  * @cfg {String} hrefTarget target frame for the link
17082  * @cfg {String} qtip An Ext QuickTip for the node
17083  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17084  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17085  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17086  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17087  * (defaults to undefined with no checkbox rendered)
17088  * @constructor
17089  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17090  */
17091 Roo.tree.TreeNode = function(attributes){
17092     attributes = attributes || {};
17093     if(typeof attributes == "string"){
17094         attributes = {text: attributes};
17095     }
17096     this.childrenRendered = false;
17097     this.rendered = false;
17098     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17099     this.expanded = attributes.expanded === true;
17100     this.isTarget = attributes.isTarget !== false;
17101     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17102     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17103
17104     /**
17105      * Read-only. The text for this node. To change it use setText().
17106      * @type String
17107      */
17108     this.text = attributes.text;
17109     /**
17110      * True if this node is disabled.
17111      * @type Boolean
17112      */
17113     this.disabled = attributes.disabled === true;
17114
17115     this.addEvents({
17116         /**
17117         * @event textchange
17118         * Fires when the text for this node is changed
17119         * @param {Node} this This node
17120         * @param {String} text The new text
17121         * @param {String} oldText The old text
17122         */
17123         "textchange" : true,
17124         /**
17125         * @event beforeexpand
17126         * Fires before this node is expanded, return false to cancel.
17127         * @param {Node} this This node
17128         * @param {Boolean} deep
17129         * @param {Boolean} anim
17130         */
17131         "beforeexpand" : true,
17132         /**
17133         * @event beforecollapse
17134         * Fires before this node is collapsed, return false to cancel.
17135         * @param {Node} this This node
17136         * @param {Boolean} deep
17137         * @param {Boolean} anim
17138         */
17139         "beforecollapse" : true,
17140         /**
17141         * @event expand
17142         * Fires when this node is expanded
17143         * @param {Node} this This node
17144         */
17145         "expand" : true,
17146         /**
17147         * @event disabledchange
17148         * Fires when the disabled status of this node changes
17149         * @param {Node} this This node
17150         * @param {Boolean} disabled
17151         */
17152         "disabledchange" : true,
17153         /**
17154         * @event collapse
17155         * Fires when this node is collapsed
17156         * @param {Node} this This node
17157         */
17158         "collapse" : true,
17159         /**
17160         * @event beforeclick
17161         * Fires before click processing. Return false to cancel the default action.
17162         * @param {Node} this This node
17163         * @param {Roo.EventObject} e The event object
17164         */
17165         "beforeclick":true,
17166         /**
17167         * @event checkchange
17168         * Fires when a node with a checkbox's checked property changes
17169         * @param {Node} this This node
17170         * @param {Boolean} checked
17171         */
17172         "checkchange":true,
17173         /**
17174         * @event click
17175         * Fires when this node is clicked
17176         * @param {Node} this This node
17177         * @param {Roo.EventObject} e The event object
17178         */
17179         "click":true,
17180         /**
17181         * @event dblclick
17182         * Fires when this node is double clicked
17183         * @param {Node} this This node
17184         * @param {Roo.EventObject} e The event object
17185         */
17186         "dblclick":true,
17187         /**
17188         * @event contextmenu
17189         * Fires when this node is right clicked
17190         * @param {Node} this This node
17191         * @param {Roo.EventObject} e The event object
17192         */
17193         "contextmenu":true,
17194         /**
17195         * @event beforechildrenrendered
17196         * Fires right before the child nodes for this node are rendered
17197         * @param {Node} this This node
17198         */
17199         "beforechildrenrendered":true
17200     });
17201
17202     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17203
17204     /**
17205      * Read-only. The UI for this node
17206      * @type TreeNodeUI
17207      */
17208     this.ui = new uiClass(this);
17209 };
17210 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17211     preventHScroll: true,
17212     /**
17213      * Returns true if this node is expanded
17214      * @return {Boolean}
17215      */
17216     isExpanded : function(){
17217         return this.expanded;
17218     },
17219
17220     /**
17221      * Returns the UI object for this node
17222      * @return {TreeNodeUI}
17223      */
17224     getUI : function(){
17225         return this.ui;
17226     },
17227
17228     // private override
17229     setFirstChild : function(node){
17230         var of = this.firstChild;
17231         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17232         if(this.childrenRendered && of && node != of){
17233             of.renderIndent(true, true);
17234         }
17235         if(this.rendered){
17236             this.renderIndent(true, true);
17237         }
17238     },
17239
17240     // private override
17241     setLastChild : function(node){
17242         var ol = this.lastChild;
17243         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17244         if(this.childrenRendered && ol && node != ol){
17245             ol.renderIndent(true, true);
17246         }
17247         if(this.rendered){
17248             this.renderIndent(true, true);
17249         }
17250     },
17251
17252     // these methods are overridden to provide lazy rendering support
17253     // private override
17254     appendChild : function(){
17255         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17256         if(node && this.childrenRendered){
17257             node.render();
17258         }
17259         this.ui.updateExpandIcon();
17260         return node;
17261     },
17262
17263     // private override
17264     removeChild : function(node){
17265         this.ownerTree.getSelectionModel().unselect(node);
17266         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17267         // if it's been rendered remove dom node
17268         if(this.childrenRendered){
17269             node.ui.remove();
17270         }
17271         if(this.childNodes.length < 1){
17272             this.collapse(false, false);
17273         }else{
17274             this.ui.updateExpandIcon();
17275         }
17276         if(!this.firstChild) {
17277             this.childrenRendered = false;
17278         }
17279         return node;
17280     },
17281
17282     // private override
17283     insertBefore : function(node, refNode){
17284         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17285         if(newNode && refNode && this.childrenRendered){
17286             node.render();
17287         }
17288         this.ui.updateExpandIcon();
17289         return newNode;
17290     },
17291
17292     /**
17293      * Sets the text for this node
17294      * @param {String} text
17295      */
17296     setText : function(text){
17297         var oldText = this.text;
17298         this.text = text;
17299         this.attributes.text = text;
17300         if(this.rendered){ // event without subscribing
17301             this.ui.onTextChange(this, text, oldText);
17302         }
17303         this.fireEvent("textchange", this, text, oldText);
17304     },
17305
17306     /**
17307      * Triggers selection of this node
17308      */
17309     select : function(){
17310         this.getOwnerTree().getSelectionModel().select(this);
17311     },
17312
17313     /**
17314      * Triggers deselection of this node
17315      */
17316     unselect : function(){
17317         this.getOwnerTree().getSelectionModel().unselect(this);
17318     },
17319
17320     /**
17321      * Returns true if this node is selected
17322      * @return {Boolean}
17323      */
17324     isSelected : function(){
17325         return this.getOwnerTree().getSelectionModel().isSelected(this);
17326     },
17327
17328     /**
17329      * Expand this node.
17330      * @param {Boolean} deep (optional) True to expand all children as well
17331      * @param {Boolean} anim (optional) false to cancel the default animation
17332      * @param {Function} callback (optional) A callback to be called when
17333      * expanding this node completes (does not wait for deep expand to complete).
17334      * Called with 1 parameter, this node.
17335      */
17336     expand : function(deep, anim, callback){
17337         if(!this.expanded){
17338             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17339                 return;
17340             }
17341             if(!this.childrenRendered){
17342                 this.renderChildren();
17343             }
17344             this.expanded = true;
17345             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17346                 this.ui.animExpand(function(){
17347                     this.fireEvent("expand", this);
17348                     if(typeof callback == "function"){
17349                         callback(this);
17350                     }
17351                     if(deep === true){
17352                         this.expandChildNodes(true);
17353                     }
17354                 }.createDelegate(this));
17355                 return;
17356             }else{
17357                 this.ui.expand();
17358                 this.fireEvent("expand", this);
17359                 if(typeof callback == "function"){
17360                     callback(this);
17361                 }
17362             }
17363         }else{
17364            if(typeof callback == "function"){
17365                callback(this);
17366            }
17367         }
17368         if(deep === true){
17369             this.expandChildNodes(true);
17370         }
17371     },
17372
17373     isHiddenRoot : function(){
17374         return this.isRoot && !this.getOwnerTree().rootVisible;
17375     },
17376
17377     /**
17378      * Collapse this node.
17379      * @param {Boolean} deep (optional) True to collapse all children as well
17380      * @param {Boolean} anim (optional) false to cancel the default animation
17381      */
17382     collapse : function(deep, anim){
17383         if(this.expanded && !this.isHiddenRoot()){
17384             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17385                 return;
17386             }
17387             this.expanded = false;
17388             if((this.getOwnerTree().animate && anim !== false) || anim){
17389                 this.ui.animCollapse(function(){
17390                     this.fireEvent("collapse", this);
17391                     if(deep === true){
17392                         this.collapseChildNodes(true);
17393                     }
17394                 }.createDelegate(this));
17395                 return;
17396             }else{
17397                 this.ui.collapse();
17398                 this.fireEvent("collapse", this);
17399             }
17400         }
17401         if(deep === true){
17402             var cs = this.childNodes;
17403             for(var i = 0, len = cs.length; i < len; i++) {
17404                 cs[i].collapse(true, false);
17405             }
17406         }
17407     },
17408
17409     // private
17410     delayedExpand : function(delay){
17411         if(!this.expandProcId){
17412             this.expandProcId = this.expand.defer(delay, this);
17413         }
17414     },
17415
17416     // private
17417     cancelExpand : function(){
17418         if(this.expandProcId){
17419             clearTimeout(this.expandProcId);
17420         }
17421         this.expandProcId = false;
17422     },
17423
17424     /**
17425      * Toggles expanded/collapsed state of the node
17426      */
17427     toggle : function(){
17428         if(this.expanded){
17429             this.collapse();
17430         }else{
17431             this.expand();
17432         }
17433     },
17434
17435     /**
17436      * Ensures all parent nodes are expanded
17437      */
17438     ensureVisible : function(callback){
17439         var tree = this.getOwnerTree();
17440         tree.expandPath(this.parentNode.getPath(), false, function(){
17441             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17442             Roo.callback(callback);
17443         }.createDelegate(this));
17444     },
17445
17446     /**
17447      * Expand all child nodes
17448      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17449      */
17450     expandChildNodes : function(deep){
17451         var cs = this.childNodes;
17452         for(var i = 0, len = cs.length; i < len; i++) {
17453                 cs[i].expand(deep);
17454         }
17455     },
17456
17457     /**
17458      * Collapse all child nodes
17459      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17460      */
17461     collapseChildNodes : function(deep){
17462         var cs = this.childNodes;
17463         for(var i = 0, len = cs.length; i < len; i++) {
17464                 cs[i].collapse(deep);
17465         }
17466     },
17467
17468     /**
17469      * Disables this node
17470      */
17471     disable : function(){
17472         this.disabled = true;
17473         this.unselect();
17474         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17475             this.ui.onDisableChange(this, true);
17476         }
17477         this.fireEvent("disabledchange", this, true);
17478     },
17479
17480     /**
17481      * Enables this node
17482      */
17483     enable : function(){
17484         this.disabled = false;
17485         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17486             this.ui.onDisableChange(this, false);
17487         }
17488         this.fireEvent("disabledchange", this, false);
17489     },
17490
17491     // private
17492     renderChildren : function(suppressEvent){
17493         if(suppressEvent !== false){
17494             this.fireEvent("beforechildrenrendered", this);
17495         }
17496         var cs = this.childNodes;
17497         for(var i = 0, len = cs.length; i < len; i++){
17498             cs[i].render(true);
17499         }
17500         this.childrenRendered = true;
17501     },
17502
17503     // private
17504     sort : function(fn, scope){
17505         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17506         if(this.childrenRendered){
17507             var cs = this.childNodes;
17508             for(var i = 0, len = cs.length; i < len; i++){
17509                 cs[i].render(true);
17510             }
17511         }
17512     },
17513
17514     // private
17515     render : function(bulkRender){
17516         this.ui.render(bulkRender);
17517         if(!this.rendered){
17518             this.rendered = true;
17519             if(this.expanded){
17520                 this.expanded = false;
17521                 this.expand(false, false);
17522             }
17523         }
17524     },
17525
17526     // private
17527     renderIndent : function(deep, refresh){
17528         if(refresh){
17529             this.ui.childIndent = null;
17530         }
17531         this.ui.renderIndent();
17532         if(deep === true && this.childrenRendered){
17533             var cs = this.childNodes;
17534             for(var i = 0, len = cs.length; i < len; i++){
17535                 cs[i].renderIndent(true, refresh);
17536             }
17537         }
17538     }
17539 });/*
17540  * Based on:
17541  * Ext JS Library 1.1.1
17542  * Copyright(c) 2006-2007, Ext JS, LLC.
17543  *
17544  * Originally Released Under LGPL - original licence link has changed is not relivant.
17545  *
17546  * Fork - LGPL
17547  * <script type="text/javascript">
17548  */
17549  
17550 /**
17551  * @class Roo.tree.AsyncTreeNode
17552  * @extends Roo.tree.TreeNode
17553  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17554  * @constructor
17555  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17556  */
17557  Roo.tree.AsyncTreeNode = function(config){
17558     this.loaded = false;
17559     this.loading = false;
17560     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17561     /**
17562     * @event beforeload
17563     * Fires before this node is loaded, return false to cancel
17564     * @param {Node} this This node
17565     */
17566     this.addEvents({'beforeload':true, 'load': true});
17567     /**
17568     * @event load
17569     * Fires when this node is loaded
17570     * @param {Node} this This node
17571     */
17572     /**
17573      * The loader used by this node (defaults to using the tree's defined loader)
17574      * @type TreeLoader
17575      * @property loader
17576      */
17577 };
17578 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17579     expand : function(deep, anim, callback){
17580         if(this.loading){ // if an async load is already running, waiting til it's done
17581             var timer;
17582             var f = function(){
17583                 if(!this.loading){ // done loading
17584                     clearInterval(timer);
17585                     this.expand(deep, anim, callback);
17586                 }
17587             }.createDelegate(this);
17588             timer = setInterval(f, 200);
17589             return;
17590         }
17591         if(!this.loaded){
17592             if(this.fireEvent("beforeload", this) === false){
17593                 return;
17594             }
17595             this.loading = true;
17596             this.ui.beforeLoad(this);
17597             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17598             if(loader){
17599                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17600                 return;
17601             }
17602         }
17603         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17604     },
17605     
17606     /**
17607      * Returns true if this node is currently loading
17608      * @return {Boolean}
17609      */
17610     isLoading : function(){
17611         return this.loading;  
17612     },
17613     
17614     loadComplete : function(deep, anim, callback){
17615         this.loading = false;
17616         this.loaded = true;
17617         this.ui.afterLoad(this);
17618         this.fireEvent("load", this);
17619         this.expand(deep, anim, callback);
17620     },
17621     
17622     /**
17623      * Returns true if this node has been loaded
17624      * @return {Boolean}
17625      */
17626     isLoaded : function(){
17627         return this.loaded;
17628     },
17629     
17630     hasChildNodes : function(){
17631         if(!this.isLeaf() && !this.loaded){
17632             return true;
17633         }else{
17634             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17635         }
17636     },
17637
17638     /**
17639      * Trigger a reload for this node
17640      * @param {Function} callback
17641      */
17642     reload : function(callback){
17643         this.collapse(false, false);
17644         while(this.firstChild){
17645             this.removeChild(this.firstChild);
17646         }
17647         this.childrenRendered = false;
17648         this.loaded = false;
17649         if(this.isHiddenRoot()){
17650             this.expanded = false;
17651         }
17652         this.expand(false, false, callback);
17653     }
17654 });/*
17655  * Based on:
17656  * Ext JS Library 1.1.1
17657  * Copyright(c) 2006-2007, Ext JS, LLC.
17658  *
17659  * Originally Released Under LGPL - original licence link has changed is not relivant.
17660  *
17661  * Fork - LGPL
17662  * <script type="text/javascript">
17663  */
17664  
17665 /**
17666  * @class Roo.tree.TreeNodeUI
17667  * @constructor
17668  * @param {Object} node The node to render
17669  * The TreeNode UI implementation is separate from the
17670  * tree implementation. Unless you are customizing the tree UI,
17671  * you should never have to use this directly.
17672  */
17673 Roo.tree.TreeNodeUI = function(node){
17674     this.node = node;
17675     this.rendered = false;
17676     this.animating = false;
17677     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17678 };
17679
17680 Roo.tree.TreeNodeUI.prototype = {
17681     removeChild : function(node){
17682         if(this.rendered){
17683             this.ctNode.removeChild(node.ui.getEl());
17684         }
17685     },
17686
17687     beforeLoad : function(){
17688          this.addClass("x-tree-node-loading");
17689     },
17690
17691     afterLoad : function(){
17692          this.removeClass("x-tree-node-loading");
17693     },
17694
17695     onTextChange : function(node, text, oldText){
17696         if(this.rendered){
17697             this.textNode.innerHTML = text;
17698         }
17699     },
17700
17701     onDisableChange : function(node, state){
17702         this.disabled = state;
17703         if(state){
17704             this.addClass("x-tree-node-disabled");
17705         }else{
17706             this.removeClass("x-tree-node-disabled");
17707         }
17708     },
17709
17710     onSelectedChange : function(state){
17711         if(state){
17712             this.focus();
17713             this.addClass("x-tree-selected");
17714         }else{
17715             //this.blur();
17716             this.removeClass("x-tree-selected");
17717         }
17718     },
17719
17720     onMove : function(tree, node, oldParent, newParent, index, refNode){
17721         this.childIndent = null;
17722         if(this.rendered){
17723             var targetNode = newParent.ui.getContainer();
17724             if(!targetNode){//target not rendered
17725                 this.holder = document.createElement("div");
17726                 this.holder.appendChild(this.wrap);
17727                 return;
17728             }
17729             var insertBefore = refNode ? refNode.ui.getEl() : null;
17730             if(insertBefore){
17731                 targetNode.insertBefore(this.wrap, insertBefore);
17732             }else{
17733                 targetNode.appendChild(this.wrap);
17734             }
17735             this.node.renderIndent(true);
17736         }
17737     },
17738
17739     addClass : function(cls){
17740         if(this.elNode){
17741             Roo.fly(this.elNode).addClass(cls);
17742         }
17743     },
17744
17745     removeClass : function(cls){
17746         if(this.elNode){
17747             Roo.fly(this.elNode).removeClass(cls);
17748         }
17749     },
17750
17751     remove : function(){
17752         if(this.rendered){
17753             this.holder = document.createElement("div");
17754             this.holder.appendChild(this.wrap);
17755         }
17756     },
17757
17758     fireEvent : function(){
17759         return this.node.fireEvent.apply(this.node, arguments);
17760     },
17761
17762     initEvents : function(){
17763         this.node.on("move", this.onMove, this);
17764         var E = Roo.EventManager;
17765         var a = this.anchor;
17766
17767         var el = Roo.fly(a, '_treeui');
17768
17769         if(Roo.isOpera){ // opera render bug ignores the CSS
17770             el.setStyle("text-decoration", "none");
17771         }
17772
17773         el.on("click", this.onClick, this);
17774         el.on("dblclick", this.onDblClick, this);
17775
17776         if(this.checkbox){
17777             Roo.EventManager.on(this.checkbox,
17778                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17779         }
17780
17781         el.on("contextmenu", this.onContextMenu, this);
17782
17783         var icon = Roo.fly(this.iconNode);
17784         icon.on("click", this.onClick, this);
17785         icon.on("dblclick", this.onDblClick, this);
17786         icon.on("contextmenu", this.onContextMenu, this);
17787         E.on(this.ecNode, "click", this.ecClick, this, true);
17788
17789         if(this.node.disabled){
17790             this.addClass("x-tree-node-disabled");
17791         }
17792         if(this.node.hidden){
17793             this.addClass("x-tree-node-disabled");
17794         }
17795         var ot = this.node.getOwnerTree();
17796         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17797         if(dd && (!this.node.isRoot || ot.rootVisible)){
17798             Roo.dd.Registry.register(this.elNode, {
17799                 node: this.node,
17800                 handles: this.getDDHandles(),
17801                 isHandle: false
17802             });
17803         }
17804     },
17805
17806     getDDHandles : function(){
17807         return [this.iconNode, this.textNode];
17808     },
17809
17810     hide : function(){
17811         if(this.rendered){
17812             this.wrap.style.display = "none";
17813         }
17814     },
17815
17816     show : function(){
17817         if(this.rendered){
17818             this.wrap.style.display = "";
17819         }
17820     },
17821
17822     onContextMenu : function(e){
17823         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17824             e.preventDefault();
17825             this.focus();
17826             this.fireEvent("contextmenu", this.node, e);
17827         }
17828     },
17829
17830     onClick : function(e){
17831         if(this.dropping){
17832             e.stopEvent();
17833             return;
17834         }
17835         if(this.fireEvent("beforeclick", this.node, e) !== false){
17836             if(!this.disabled && this.node.attributes.href){
17837                 this.fireEvent("click", this.node, e);
17838                 return;
17839             }
17840             e.preventDefault();
17841             if(this.disabled){
17842                 return;
17843             }
17844
17845             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17846                 this.node.toggle();
17847             }
17848
17849             this.fireEvent("click", this.node, e);
17850         }else{
17851             e.stopEvent();
17852         }
17853     },
17854
17855     onDblClick : function(e){
17856         e.preventDefault();
17857         if(this.disabled){
17858             return;
17859         }
17860         if(this.checkbox){
17861             this.toggleCheck();
17862         }
17863         if(!this.animating && this.node.hasChildNodes()){
17864             this.node.toggle();
17865         }
17866         this.fireEvent("dblclick", this.node, e);
17867     },
17868
17869     onCheckChange : function(){
17870         var checked = this.checkbox.checked;
17871         this.node.attributes.checked = checked;
17872         this.fireEvent('checkchange', this.node, checked);
17873     },
17874
17875     ecClick : function(e){
17876         if(!this.animating && this.node.hasChildNodes()){
17877             this.node.toggle();
17878         }
17879     },
17880
17881     startDrop : function(){
17882         this.dropping = true;
17883     },
17884
17885     // delayed drop so the click event doesn't get fired on a drop
17886     endDrop : function(){
17887        setTimeout(function(){
17888            this.dropping = false;
17889        }.createDelegate(this), 50);
17890     },
17891
17892     expand : function(){
17893         this.updateExpandIcon();
17894         this.ctNode.style.display = "";
17895     },
17896
17897     focus : function(){
17898         if(!this.node.preventHScroll){
17899             try{this.anchor.focus();
17900             }catch(e){}
17901         }else if(!Roo.isIE){
17902             try{
17903                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17904                 var l = noscroll.scrollLeft;
17905                 this.anchor.focus();
17906                 noscroll.scrollLeft = l;
17907             }catch(e){}
17908         }
17909     },
17910
17911     toggleCheck : function(value){
17912         var cb = this.checkbox;
17913         if(cb){
17914             cb.checked = (value === undefined ? !cb.checked : value);
17915         }
17916     },
17917
17918     blur : function(){
17919         try{
17920             this.anchor.blur();
17921         }catch(e){}
17922     },
17923
17924     animExpand : function(callback){
17925         var ct = Roo.get(this.ctNode);
17926         ct.stopFx();
17927         if(!this.node.hasChildNodes()){
17928             this.updateExpandIcon();
17929             this.ctNode.style.display = "";
17930             Roo.callback(callback);
17931             return;
17932         }
17933         this.animating = true;
17934         this.updateExpandIcon();
17935
17936         ct.slideIn('t', {
17937            callback : function(){
17938                this.animating = false;
17939                Roo.callback(callback);
17940             },
17941             scope: this,
17942             duration: this.node.ownerTree.duration || .25
17943         });
17944     },
17945
17946     highlight : function(){
17947         var tree = this.node.getOwnerTree();
17948         Roo.fly(this.wrap).highlight(
17949             tree.hlColor || "C3DAF9",
17950             {endColor: tree.hlBaseColor}
17951         );
17952     },
17953
17954     collapse : function(){
17955         this.updateExpandIcon();
17956         this.ctNode.style.display = "none";
17957     },
17958
17959     animCollapse : function(callback){
17960         var ct = Roo.get(this.ctNode);
17961         ct.enableDisplayMode('block');
17962         ct.stopFx();
17963
17964         this.animating = true;
17965         this.updateExpandIcon();
17966
17967         ct.slideOut('t', {
17968             callback : function(){
17969                this.animating = false;
17970                Roo.callback(callback);
17971             },
17972             scope: this,
17973             duration: this.node.ownerTree.duration || .25
17974         });
17975     },
17976
17977     getContainer : function(){
17978         return this.ctNode;
17979     },
17980
17981     getEl : function(){
17982         return this.wrap;
17983     },
17984
17985     appendDDGhost : function(ghostNode){
17986         ghostNode.appendChild(this.elNode.cloneNode(true));
17987     },
17988
17989     getDDRepairXY : function(){
17990         return Roo.lib.Dom.getXY(this.iconNode);
17991     },
17992
17993     onRender : function(){
17994         this.render();
17995     },
17996
17997     render : function(bulkRender){
17998         var n = this.node, a = n.attributes;
17999         var targetNode = n.parentNode ?
18000               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18001
18002         if(!this.rendered){
18003             this.rendered = true;
18004
18005             this.renderElements(n, a, targetNode, bulkRender);
18006
18007             if(a.qtip){
18008                if(this.textNode.setAttributeNS){
18009                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18010                    if(a.qtipTitle){
18011                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18012                    }
18013                }else{
18014                    this.textNode.setAttribute("ext:qtip", a.qtip);
18015                    if(a.qtipTitle){
18016                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18017                    }
18018                }
18019             }else if(a.qtipCfg){
18020                 a.qtipCfg.target = Roo.id(this.textNode);
18021                 Roo.QuickTips.register(a.qtipCfg);
18022             }
18023             this.initEvents();
18024             if(!this.node.expanded){
18025                 this.updateExpandIcon();
18026             }
18027         }else{
18028             if(bulkRender === true) {
18029                 targetNode.appendChild(this.wrap);
18030             }
18031         }
18032     },
18033
18034     renderElements : function(n, a, targetNode, bulkRender){
18035         // add some indent caching, this helps performance when rendering a large tree
18036         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18037         var t = n.getOwnerTree();
18038         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18039         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18040         var cb = typeof a.checked == 'boolean';
18041         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18042         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18043             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18044             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18045             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18046             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18047             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18048              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18049                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18050             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18051             "</li>"];
18052
18053         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18054             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18055                                 n.nextSibling.ui.getEl(), buf.join(""));
18056         }else{
18057             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18058         }
18059
18060         this.elNode = this.wrap.childNodes[0];
18061         this.ctNode = this.wrap.childNodes[1];
18062         var cs = this.elNode.childNodes;
18063         this.indentNode = cs[0];
18064         this.ecNode = cs[1];
18065         this.iconNode = cs[2];
18066         var index = 3;
18067         if(cb){
18068             this.checkbox = cs[3];
18069             index++;
18070         }
18071         this.anchor = cs[index];
18072         this.textNode = cs[index].firstChild;
18073     },
18074
18075     getAnchor : function(){
18076         return this.anchor;
18077     },
18078
18079     getTextEl : function(){
18080         return this.textNode;
18081     },
18082
18083     getIconEl : function(){
18084         return this.iconNode;
18085     },
18086
18087     isChecked : function(){
18088         return this.checkbox ? this.checkbox.checked : false;
18089     },
18090
18091     updateExpandIcon : function(){
18092         if(this.rendered){
18093             var n = this.node, c1, c2;
18094             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18095             var hasChild = n.hasChildNodes();
18096             if(hasChild){
18097                 if(n.expanded){
18098                     cls += "-minus";
18099                     c1 = "x-tree-node-collapsed";
18100                     c2 = "x-tree-node-expanded";
18101                 }else{
18102                     cls += "-plus";
18103                     c1 = "x-tree-node-expanded";
18104                     c2 = "x-tree-node-collapsed";
18105                 }
18106                 if(this.wasLeaf){
18107                     this.removeClass("x-tree-node-leaf");
18108                     this.wasLeaf = false;
18109                 }
18110                 if(this.c1 != c1 || this.c2 != c2){
18111                     Roo.fly(this.elNode).replaceClass(c1, c2);
18112                     this.c1 = c1; this.c2 = c2;
18113                 }
18114             }else{
18115                 if(!this.wasLeaf){
18116                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18117                     delete this.c1;
18118                     delete this.c2;
18119                     this.wasLeaf = true;
18120                 }
18121             }
18122             var ecc = "x-tree-ec-icon "+cls;
18123             if(this.ecc != ecc){
18124                 this.ecNode.className = ecc;
18125                 this.ecc = ecc;
18126             }
18127         }
18128     },
18129
18130     getChildIndent : function(){
18131         if(!this.childIndent){
18132             var buf = [];
18133             var p = this.node;
18134             while(p){
18135                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18136                     if(!p.isLast()) {
18137                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18138                     } else {
18139                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18140                     }
18141                 }
18142                 p = p.parentNode;
18143             }
18144             this.childIndent = buf.join("");
18145         }
18146         return this.childIndent;
18147     },
18148
18149     renderIndent : function(){
18150         if(this.rendered){
18151             var indent = "";
18152             var p = this.node.parentNode;
18153             if(p){
18154                 indent = p.ui.getChildIndent();
18155             }
18156             if(this.indentMarkup != indent){ // don't rerender if not required
18157                 this.indentNode.innerHTML = indent;
18158                 this.indentMarkup = indent;
18159             }
18160             this.updateExpandIcon();
18161         }
18162     }
18163 };
18164
18165 Roo.tree.RootTreeNodeUI = function(){
18166     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18167 };
18168 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18169     render : function(){
18170         if(!this.rendered){
18171             var targetNode = this.node.ownerTree.innerCt.dom;
18172             this.node.expanded = true;
18173             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18174             this.wrap = this.ctNode = targetNode.firstChild;
18175         }
18176     },
18177     collapse : function(){
18178     },
18179     expand : function(){
18180     }
18181 });/*
18182  * Based on:
18183  * Ext JS Library 1.1.1
18184  * Copyright(c) 2006-2007, Ext JS, LLC.
18185  *
18186  * Originally Released Under LGPL - original licence link has changed is not relivant.
18187  *
18188  * Fork - LGPL
18189  * <script type="text/javascript">
18190  */
18191 /**
18192  * @class Roo.tree.TreeLoader
18193  * @extends Roo.util.Observable
18194  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18195  * nodes from a specified URL. The response must be a javascript Array definition
18196  * who's elements are node definition objects. eg:
18197  * <pre><code>
18198    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18199     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18200 </code></pre>
18201  * <br><br>
18202  * A server request is sent, and child nodes are loaded only when a node is expanded.
18203  * The loading node's id is passed to the server under the parameter name "node" to
18204  * enable the server to produce the correct child nodes.
18205  * <br><br>
18206  * To pass extra parameters, an event handler may be attached to the "beforeload"
18207  * event, and the parameters specified in the TreeLoader's baseParams property:
18208  * <pre><code>
18209     myTreeLoader.on("beforeload", function(treeLoader, node) {
18210         this.baseParams.category = node.attributes.category;
18211     }, this);
18212 </code></pre><
18213  * This would pass an HTTP parameter called "category" to the server containing
18214  * the value of the Node's "category" attribute.
18215  * @constructor
18216  * Creates a new Treeloader.
18217  * @param {Object} config A config object containing config properties.
18218  */
18219 Roo.tree.TreeLoader = function(config){
18220     this.baseParams = {};
18221     this.requestMethod = "POST";
18222     Roo.apply(this, config);
18223
18224     this.addEvents({
18225     
18226         /**
18227          * @event beforeload
18228          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18229          * @param {Object} This TreeLoader object.
18230          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18231          * @param {Object} callback The callback function specified in the {@link #load} call.
18232          */
18233         beforeload : true,
18234         /**
18235          * @event load
18236          * Fires when the node has been successfuly loaded.
18237          * @param {Object} This TreeLoader object.
18238          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18239          * @param {Object} response The response object containing the data from the server.
18240          */
18241         load : true,
18242         /**
18243          * @event loadexception
18244          * Fires if the network request failed.
18245          * @param {Object} This TreeLoader object.
18246          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18247          * @param {Object} response The response object containing the data from the server.
18248          */
18249         loadexception : true,
18250         /**
18251          * @event create
18252          * Fires before a node is created, enabling you to return custom Node types 
18253          * @param {Object} This TreeLoader object.
18254          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18255          */
18256         create : true
18257     });
18258
18259     Roo.tree.TreeLoader.superclass.constructor.call(this);
18260 };
18261
18262 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18263     /**
18264     * @cfg {String} dataUrl The URL from which to request a Json string which
18265     * specifies an array of node definition object representing the child nodes
18266     * to be loaded.
18267     */
18268     /**
18269     * @cfg {Object} baseParams (optional) An object containing properties which
18270     * specify HTTP parameters to be passed to each request for child nodes.
18271     */
18272     /**
18273     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18274     * created by this loader. If the attributes sent by the server have an attribute in this object,
18275     * they take priority.
18276     */
18277     /**
18278     * @cfg {Object} uiProviders (optional) An object containing properties which
18279     * 
18280     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18281     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18282     * <i>uiProvider</i> attribute of a returned child node is a string rather
18283     * than a reference to a TreeNodeUI implementation, this that string value
18284     * is used as a property name in the uiProviders object. You can define the provider named
18285     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18286     */
18287     uiProviders : {},
18288
18289     /**
18290     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18291     * child nodes before loading.
18292     */
18293     clearOnLoad : true,
18294
18295     /**
18296     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18297     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18298     * Grid query { data : [ .....] }
18299     */
18300     
18301     root : false,
18302      /**
18303     * @cfg {String} queryParam (optional) 
18304     * Name of the query as it will be passed on the querystring (defaults to 'node')
18305     * eg. the request will be ?node=[id]
18306     */
18307     
18308     
18309     queryParam: false,
18310     
18311     /**
18312      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18313      * This is called automatically when a node is expanded, but may be used to reload
18314      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18315      * @param {Roo.tree.TreeNode} node
18316      * @param {Function} callback
18317      */
18318     load : function(node, callback){
18319         if(this.clearOnLoad){
18320             while(node.firstChild){
18321                 node.removeChild(node.firstChild);
18322             }
18323         }
18324         if(node.attributes.children){ // preloaded json children
18325             var cs = node.attributes.children;
18326             for(var i = 0, len = cs.length; i < len; i++){
18327                 node.appendChild(this.createNode(cs[i]));
18328             }
18329             if(typeof callback == "function"){
18330                 callback();
18331             }
18332         }else if(this.dataUrl){
18333             this.requestData(node, callback);
18334         }
18335     },
18336
18337     getParams: function(node){
18338         var buf = [], bp = this.baseParams;
18339         for(var key in bp){
18340             if(typeof bp[key] != "function"){
18341                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18342             }
18343         }
18344         var n = this.queryParam === false ? 'node' : this.queryParam;
18345         buf.push(n + "=", encodeURIComponent(node.id));
18346         return buf.join("");
18347     },
18348
18349     requestData : function(node, callback){
18350         if(this.fireEvent("beforeload", this, node, callback) !== false){
18351             this.transId = Roo.Ajax.request({
18352                 method:this.requestMethod,
18353                 url: this.dataUrl||this.url,
18354                 success: this.handleResponse,
18355                 failure: this.handleFailure,
18356                 scope: this,
18357                 argument: {callback: callback, node: node},
18358                 params: this.getParams(node)
18359             });
18360         }else{
18361             // if the load is cancelled, make sure we notify
18362             // the node that we are done
18363             if(typeof callback == "function"){
18364                 callback();
18365             }
18366         }
18367     },
18368
18369     isLoading : function(){
18370         return this.transId ? true : false;
18371     },
18372
18373     abort : function(){
18374         if(this.isLoading()){
18375             Roo.Ajax.abort(this.transId);
18376         }
18377     },
18378
18379     // private
18380     createNode : function(attr){
18381         // apply baseAttrs, nice idea Corey!
18382         if(this.baseAttrs){
18383             Roo.applyIf(attr, this.baseAttrs);
18384         }
18385         if(this.applyLoader !== false){
18386             attr.loader = this;
18387         }
18388         // uiProvider = depreciated..
18389         
18390         if(typeof(attr.uiProvider) == 'string'){
18391            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18392                 /**  eval:var:attr */ eval(attr.uiProvider);
18393         }
18394         if(typeof(this.uiProviders['default']) != 'undefined') {
18395             attr.uiProvider = this.uiProviders['default'];
18396         }
18397         
18398         this.fireEvent('create', this, attr);
18399         
18400         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18401         return(attr.leaf ?
18402                         new Roo.tree.TreeNode(attr) :
18403                         new Roo.tree.AsyncTreeNode(attr));
18404     },
18405
18406     processResponse : function(response, node, callback){
18407         var json = response.responseText;
18408         try {
18409             
18410             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18411             if (this.root !== false) {
18412                 o = o[this.root];
18413             }
18414             
18415             for(var i = 0, len = o.length; i < len; i++){
18416                 var n = this.createNode(o[i]);
18417                 if(n){
18418                     node.appendChild(n);
18419                 }
18420             }
18421             if(typeof callback == "function"){
18422                 callback(this, node);
18423             }
18424         }catch(e){
18425             this.handleFailure(response);
18426         }
18427     },
18428
18429     handleResponse : function(response){
18430         this.transId = false;
18431         var a = response.argument;
18432         this.processResponse(response, a.node, a.callback);
18433         this.fireEvent("load", this, a.node, response);
18434     },
18435
18436     handleFailure : function(response){
18437         this.transId = false;
18438         var a = response.argument;
18439         this.fireEvent("loadexception", this, a.node, response);
18440         if(typeof a.callback == "function"){
18441             a.callback(this, a.node);
18442         }
18443     }
18444 });/*
18445  * Based on:
18446  * Ext JS Library 1.1.1
18447  * Copyright(c) 2006-2007, Ext JS, LLC.
18448  *
18449  * Originally Released Under LGPL - original licence link has changed is not relivant.
18450  *
18451  * Fork - LGPL
18452  * <script type="text/javascript">
18453  */
18454
18455 /**
18456 * @class Roo.tree.TreeFilter
18457 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18458 * @param {TreePanel} tree
18459 * @param {Object} config (optional)
18460  */
18461 Roo.tree.TreeFilter = function(tree, config){
18462     this.tree = tree;
18463     this.filtered = {};
18464     Roo.apply(this, config);
18465 };
18466
18467 Roo.tree.TreeFilter.prototype = {
18468     clearBlank:false,
18469     reverse:false,
18470     autoClear:false,
18471     remove:false,
18472
18473      /**
18474      * Filter the data by a specific attribute.
18475      * @param {String/RegExp} value Either string that the attribute value
18476      * should start with or a RegExp to test against the attribute
18477      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18478      * @param {TreeNode} startNode (optional) The node to start the filter at.
18479      */
18480     filter : function(value, attr, startNode){
18481         attr = attr || "text";
18482         var f;
18483         if(typeof value == "string"){
18484             var vlen = value.length;
18485             // auto clear empty filter
18486             if(vlen == 0 && this.clearBlank){
18487                 this.clear();
18488                 return;
18489             }
18490             value = value.toLowerCase();
18491             f = function(n){
18492                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18493             };
18494         }else if(value.exec){ // regex?
18495             f = function(n){
18496                 return value.test(n.attributes[attr]);
18497             };
18498         }else{
18499             throw 'Illegal filter type, must be string or regex';
18500         }
18501         this.filterBy(f, null, startNode);
18502         },
18503
18504     /**
18505      * Filter by a function. The passed function will be called with each
18506      * node in the tree (or from the startNode). If the function returns true, the node is kept
18507      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18508      * @param {Function} fn The filter function
18509      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18510      */
18511     filterBy : function(fn, scope, startNode){
18512         startNode = startNode || this.tree.root;
18513         if(this.autoClear){
18514             this.clear();
18515         }
18516         var af = this.filtered, rv = this.reverse;
18517         var f = function(n){
18518             if(n == startNode){
18519                 return true;
18520             }
18521             if(af[n.id]){
18522                 return false;
18523             }
18524             var m = fn.call(scope || n, n);
18525             if(!m || rv){
18526                 af[n.id] = n;
18527                 n.ui.hide();
18528                 return false;
18529             }
18530             return true;
18531         };
18532         startNode.cascade(f);
18533         if(this.remove){
18534            for(var id in af){
18535                if(typeof id != "function"){
18536                    var n = af[id];
18537                    if(n && n.parentNode){
18538                        n.parentNode.removeChild(n);
18539                    }
18540                }
18541            }
18542         }
18543     },
18544
18545     /**
18546      * Clears the current filter. Note: with the "remove" option
18547      * set a filter cannot be cleared.
18548      */
18549     clear : function(){
18550         var t = this.tree;
18551         var af = this.filtered;
18552         for(var id in af){
18553             if(typeof id != "function"){
18554                 var n = af[id];
18555                 if(n){
18556                     n.ui.show();
18557                 }
18558             }
18559         }
18560         this.filtered = {};
18561     }
18562 };
18563 /*
18564  * Based on:
18565  * Ext JS Library 1.1.1
18566  * Copyright(c) 2006-2007, Ext JS, LLC.
18567  *
18568  * Originally Released Under LGPL - original licence link has changed is not relivant.
18569  *
18570  * Fork - LGPL
18571  * <script type="text/javascript">
18572  */
18573  
18574
18575 /**
18576  * @class Roo.tree.TreeSorter
18577  * Provides sorting of nodes in a TreePanel
18578  * 
18579  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18580  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18581  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18582  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18583  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18584  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18585  * @constructor
18586  * @param {TreePanel} tree
18587  * @param {Object} config
18588  */
18589 Roo.tree.TreeSorter = function(tree, config){
18590     Roo.apply(this, config);
18591     tree.on("beforechildrenrendered", this.doSort, this);
18592     tree.on("append", this.updateSort, this);
18593     tree.on("insert", this.updateSort, this);
18594     
18595     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18596     var p = this.property || "text";
18597     var sortType = this.sortType;
18598     var fs = this.folderSort;
18599     var cs = this.caseSensitive === true;
18600     var leafAttr = this.leafAttr || 'leaf';
18601
18602     this.sortFn = function(n1, n2){
18603         if(fs){
18604             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18605                 return 1;
18606             }
18607             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18608                 return -1;
18609             }
18610         }
18611         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18612         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18613         if(v1 < v2){
18614                         return dsc ? +1 : -1;
18615                 }else if(v1 > v2){
18616                         return dsc ? -1 : +1;
18617         }else{
18618                 return 0;
18619         }
18620     };
18621 };
18622
18623 Roo.tree.TreeSorter.prototype = {
18624     doSort : function(node){
18625         node.sort(this.sortFn);
18626     },
18627     
18628     compareNodes : function(n1, n2){
18629         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18630     },
18631     
18632     updateSort : function(tree, node){
18633         if(node.childrenRendered){
18634             this.doSort.defer(1, this, [node]);
18635         }
18636     }
18637 };/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647
18648 if(Roo.dd.DropZone){
18649     
18650 Roo.tree.TreeDropZone = function(tree, config){
18651     this.allowParentInsert = false;
18652     this.allowContainerDrop = false;
18653     this.appendOnly = false;
18654     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18655     this.tree = tree;
18656     this.lastInsertClass = "x-tree-no-status";
18657     this.dragOverData = {};
18658 };
18659
18660 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18661     ddGroup : "TreeDD",
18662     
18663     expandDelay : 1000,
18664     
18665     expandNode : function(node){
18666         if(node.hasChildNodes() && !node.isExpanded()){
18667             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18668         }
18669     },
18670     
18671     queueExpand : function(node){
18672         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18673     },
18674     
18675     cancelExpand : function(){
18676         if(this.expandProcId){
18677             clearTimeout(this.expandProcId);
18678             this.expandProcId = false;
18679         }
18680     },
18681     
18682     isValidDropPoint : function(n, pt, dd, e, data){
18683         if(!n || !data){ return false; }
18684         var targetNode = n.node;
18685         var dropNode = data.node;
18686         // default drop rules
18687         if(!(targetNode && targetNode.isTarget && pt)){
18688             return false;
18689         }
18690         if(pt == "append" && targetNode.allowChildren === false){
18691             return false;
18692         }
18693         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18694             return false;
18695         }
18696         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18697             return false;
18698         }
18699         // reuse the object
18700         var overEvent = this.dragOverData;
18701         overEvent.tree = this.tree;
18702         overEvent.target = targetNode;
18703         overEvent.data = data;
18704         overEvent.point = pt;
18705         overEvent.source = dd;
18706         overEvent.rawEvent = e;
18707         overEvent.dropNode = dropNode;
18708         overEvent.cancel = false;  
18709         var result = this.tree.fireEvent("nodedragover", overEvent);
18710         return overEvent.cancel === false && result !== false;
18711     },
18712     
18713     getDropPoint : function(e, n, dd){
18714         var tn = n.node;
18715         if(tn.isRoot){
18716             return tn.allowChildren !== false ? "append" : false; // always append for root
18717         }
18718         var dragEl = n.ddel;
18719         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18720         var y = Roo.lib.Event.getPageY(e);
18721         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18722         
18723         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18724         var noAppend = tn.allowChildren === false;
18725         if(this.appendOnly || tn.parentNode.allowChildren === false){
18726             return noAppend ? false : "append";
18727         }
18728         var noBelow = false;
18729         if(!this.allowParentInsert){
18730             noBelow = tn.hasChildNodes() && tn.isExpanded();
18731         }
18732         var q = (b - t) / (noAppend ? 2 : 3);
18733         if(y >= t && y < (t + q)){
18734             return "above";
18735         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18736             return "below";
18737         }else{
18738             return "append";
18739         }
18740     },
18741     
18742     onNodeEnter : function(n, dd, e, data){
18743         this.cancelExpand();
18744     },
18745     
18746     onNodeOver : function(n, dd, e, data){
18747         var pt = this.getDropPoint(e, n, dd);
18748         var node = n.node;
18749         
18750         // auto node expand check
18751         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18752             this.queueExpand(node);
18753         }else if(pt != "append"){
18754             this.cancelExpand();
18755         }
18756         
18757         // set the insert point style on the target node
18758         var returnCls = this.dropNotAllowed;
18759         if(this.isValidDropPoint(n, pt, dd, e, data)){
18760            if(pt){
18761                var el = n.ddel;
18762                var cls;
18763                if(pt == "above"){
18764                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18765                    cls = "x-tree-drag-insert-above";
18766                }else if(pt == "below"){
18767                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18768                    cls = "x-tree-drag-insert-below";
18769                }else{
18770                    returnCls = "x-tree-drop-ok-append";
18771                    cls = "x-tree-drag-append";
18772                }
18773                if(this.lastInsertClass != cls){
18774                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18775                    this.lastInsertClass = cls;
18776                }
18777            }
18778        }
18779        return returnCls;
18780     },
18781     
18782     onNodeOut : function(n, dd, e, data){
18783         this.cancelExpand();
18784         this.removeDropIndicators(n);
18785     },
18786     
18787     onNodeDrop : function(n, dd, e, data){
18788         var point = this.getDropPoint(e, n, dd);
18789         var targetNode = n.node;
18790         targetNode.ui.startDrop();
18791         if(!this.isValidDropPoint(n, point, dd, e, data)){
18792             targetNode.ui.endDrop();
18793             return false;
18794         }
18795         // first try to find the drop node
18796         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18797         var dropEvent = {
18798             tree : this.tree,
18799             target: targetNode,
18800             data: data,
18801             point: point,
18802             source: dd,
18803             rawEvent: e,
18804             dropNode: dropNode,
18805             cancel: !dropNode   
18806         };
18807         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18808         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18809             targetNode.ui.endDrop();
18810             return false;
18811         }
18812         // allow target changing
18813         targetNode = dropEvent.target;
18814         if(point == "append" && !targetNode.isExpanded()){
18815             targetNode.expand(false, null, function(){
18816                 this.completeDrop(dropEvent);
18817             }.createDelegate(this));
18818         }else{
18819             this.completeDrop(dropEvent);
18820         }
18821         return true;
18822     },
18823     
18824     completeDrop : function(de){
18825         var ns = de.dropNode, p = de.point, t = de.target;
18826         if(!(ns instanceof Array)){
18827             ns = [ns];
18828         }
18829         var n;
18830         for(var i = 0, len = ns.length; i < len; i++){
18831             n = ns[i];
18832             if(p == "above"){
18833                 t.parentNode.insertBefore(n, t);
18834             }else if(p == "below"){
18835                 t.parentNode.insertBefore(n, t.nextSibling);
18836             }else{
18837                 t.appendChild(n);
18838             }
18839         }
18840         n.ui.focus();
18841         if(this.tree.hlDrop){
18842             n.ui.highlight();
18843         }
18844         t.ui.endDrop();
18845         this.tree.fireEvent("nodedrop", de);
18846     },
18847     
18848     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18849         if(this.tree.hlDrop){
18850             dropNode.ui.focus();
18851             dropNode.ui.highlight();
18852         }
18853         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18854     },
18855     
18856     getTree : function(){
18857         return this.tree;
18858     },
18859     
18860     removeDropIndicators : function(n){
18861         if(n && n.ddel){
18862             var el = n.ddel;
18863             Roo.fly(el).removeClass([
18864                     "x-tree-drag-insert-above",
18865                     "x-tree-drag-insert-below",
18866                     "x-tree-drag-append"]);
18867             this.lastInsertClass = "_noclass";
18868         }
18869     },
18870     
18871     beforeDragDrop : function(target, e, id){
18872         this.cancelExpand();
18873         return true;
18874     },
18875     
18876     afterRepair : function(data){
18877         if(data && Roo.enableFx){
18878             data.node.ui.highlight();
18879         }
18880         this.hideProxy();
18881     }    
18882 });
18883
18884 }
18885 /*
18886  * Based on:
18887  * Ext JS Library 1.1.1
18888  * Copyright(c) 2006-2007, Ext JS, LLC.
18889  *
18890  * Originally Released Under LGPL - original licence link has changed is not relivant.
18891  *
18892  * Fork - LGPL
18893  * <script type="text/javascript">
18894  */
18895  
18896
18897 if(Roo.dd.DragZone){
18898 Roo.tree.TreeDragZone = function(tree, config){
18899     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18900     this.tree = tree;
18901 };
18902
18903 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18904     ddGroup : "TreeDD",
18905     
18906     onBeforeDrag : function(data, e){
18907         var n = data.node;
18908         return n && n.draggable && !n.disabled;
18909     },
18910     
18911     onInitDrag : function(e){
18912         var data = this.dragData;
18913         this.tree.getSelectionModel().select(data.node);
18914         this.proxy.update("");
18915         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18916         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18917     },
18918     
18919     getRepairXY : function(e, data){
18920         return data.node.ui.getDDRepairXY();
18921     },
18922     
18923     onEndDrag : function(data, e){
18924         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18925     },
18926     
18927     onValidDrop : function(dd, e, id){
18928         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18929         this.hideProxy();
18930     },
18931     
18932     beforeInvalidDrop : function(e, id){
18933         // this scrolls the original position back into view
18934         var sm = this.tree.getSelectionModel();
18935         sm.clearSelections();
18936         sm.select(this.dragData.node);
18937     }
18938 });
18939 }/*
18940  * Based on:
18941  * Ext JS Library 1.1.1
18942  * Copyright(c) 2006-2007, Ext JS, LLC.
18943  *
18944  * Originally Released Under LGPL - original licence link has changed is not relivant.
18945  *
18946  * Fork - LGPL
18947  * <script type="text/javascript">
18948  */
18949 /**
18950  * @class Roo.tree.TreeEditor
18951  * @extends Roo.Editor
18952  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18953  * as the editor field.
18954  * @constructor
18955  * @param {TreePanel} tree
18956  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18957  */
18958 Roo.tree.TreeEditor = function(tree, config){
18959     config = config || {};
18960     var field = config.events ? config : new Roo.form.TextField(config);
18961     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18962
18963     this.tree = tree;
18964
18965     tree.on('beforeclick', this.beforeNodeClick, this);
18966     tree.getTreeEl().on('mousedown', this.hide, this);
18967     this.on('complete', this.updateNode, this);
18968     this.on('beforestartedit', this.fitToTree, this);
18969     this.on('startedit', this.bindScroll, this, {delay:10});
18970     this.on('specialkey', this.onSpecialKey, this);
18971 };
18972
18973 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18974     /**
18975      * @cfg {String} alignment
18976      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18977      */
18978     alignment: "l-l",
18979     // inherit
18980     autoSize: false,
18981     /**
18982      * @cfg {Boolean} hideEl
18983      * True to hide the bound element while the editor is displayed (defaults to false)
18984      */
18985     hideEl : false,
18986     /**
18987      * @cfg {String} cls
18988      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18989      */
18990     cls: "x-small-editor x-tree-editor",
18991     /**
18992      * @cfg {Boolean} shim
18993      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18994      */
18995     shim:false,
18996     // inherit
18997     shadow:"frame",
18998     /**
18999      * @cfg {Number} maxWidth
19000      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19001      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19002      * scroll and client offsets into account prior to each edit.
19003      */
19004     maxWidth: 250,
19005
19006     editDelay : 350,
19007
19008     // private
19009     fitToTree : function(ed, el){
19010         var td = this.tree.getTreeEl().dom, nd = el.dom;
19011         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19012             td.scrollLeft = nd.offsetLeft;
19013         }
19014         var w = Math.min(
19015                 this.maxWidth,
19016                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19017         this.setSize(w, '');
19018     },
19019
19020     // private
19021     triggerEdit : function(node){
19022         this.completeEdit();
19023         this.editNode = node;
19024         this.startEdit(node.ui.textNode, node.text);
19025     },
19026
19027     // private
19028     bindScroll : function(){
19029         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19030     },
19031
19032     // private
19033     beforeNodeClick : function(node, e){
19034         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19035         this.lastClick = new Date();
19036         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19037             e.stopEvent();
19038             this.triggerEdit(node);
19039             return false;
19040         }
19041     },
19042
19043     // private
19044     updateNode : function(ed, value){
19045         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19046         this.editNode.setText(value);
19047     },
19048
19049     // private
19050     onHide : function(){
19051         Roo.tree.TreeEditor.superclass.onHide.call(this);
19052         if(this.editNode){
19053             this.editNode.ui.focus();
19054         }
19055     },
19056
19057     // private
19058     onSpecialKey : function(field, e){
19059         var k = e.getKey();
19060         if(k == e.ESC){
19061             e.stopEvent();
19062             this.cancelEdit();
19063         }else if(k == e.ENTER && !e.hasModifier()){
19064             e.stopEvent();
19065             this.completeEdit();
19066         }
19067     }
19068 });//<Script type="text/javascript">
19069 /*
19070  * Based on:
19071  * Ext JS Library 1.1.1
19072  * Copyright(c) 2006-2007, Ext JS, LLC.
19073  *
19074  * Originally Released Under LGPL - original licence link has changed is not relivant.
19075  *
19076  * Fork - LGPL
19077  * <script type="text/javascript">
19078  */
19079  
19080 /**
19081  * Not documented??? - probably should be...
19082  */
19083
19084 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19085     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19086     
19087     renderElements : function(n, a, targetNode, bulkRender){
19088         //consel.log("renderElements?");
19089         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19090
19091         var t = n.getOwnerTree();
19092         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19093         
19094         var cols = t.columns;
19095         var bw = t.borderWidth;
19096         var c = cols[0];
19097         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19098          var cb = typeof a.checked == "boolean";
19099         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19100         var colcls = 'x-t-' + tid + '-c0';
19101         var buf = [
19102             '<li class="x-tree-node">',
19103             
19104                 
19105                 '<div class="x-tree-node-el ', a.cls,'">',
19106                     // extran...
19107                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19108                 
19109                 
19110                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19111                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19112                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19113                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19114                            (a.iconCls ? ' '+a.iconCls : ''),
19115                            '" unselectable="on" />',
19116                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19117                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19118                              
19119                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19120                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19121                             '<span unselectable="on" qtip="' + tx + '">',
19122                              tx,
19123                              '</span></a>' ,
19124                     '</div>',
19125                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19126                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19127                  ];
19128         for(var i = 1, len = cols.length; i < len; i++){
19129             c = cols[i];
19130             colcls = 'x-t-' + tid + '-c' +i;
19131             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19132             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19133                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19134                       "</div>");
19135          }
19136          
19137          buf.push(
19138             '</a>',
19139             '<div class="x-clear"></div></div>',
19140             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19141             "</li>");
19142         
19143         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19144             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19145                                 n.nextSibling.ui.getEl(), buf.join(""));
19146         }else{
19147             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19148         }
19149         var el = this.wrap.firstChild;
19150         this.elRow = el;
19151         this.elNode = el.firstChild;
19152         this.ranchor = el.childNodes[1];
19153         this.ctNode = this.wrap.childNodes[1];
19154         var cs = el.firstChild.childNodes;
19155         this.indentNode = cs[0];
19156         this.ecNode = cs[1];
19157         this.iconNode = cs[2];
19158         var index = 3;
19159         if(cb){
19160             this.checkbox = cs[3];
19161             index++;
19162         }
19163         this.anchor = cs[index];
19164         
19165         this.textNode = cs[index].firstChild;
19166         
19167         //el.on("click", this.onClick, this);
19168         //el.on("dblclick", this.onDblClick, this);
19169         
19170         
19171        // console.log(this);
19172     },
19173     initEvents : function(){
19174         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19175         
19176             
19177         var a = this.ranchor;
19178
19179         var el = Roo.get(a);
19180
19181         if(Roo.isOpera){ // opera render bug ignores the CSS
19182             el.setStyle("text-decoration", "none");
19183         }
19184
19185         el.on("click", this.onClick, this);
19186         el.on("dblclick", this.onDblClick, this);
19187         el.on("contextmenu", this.onContextMenu, this);
19188         
19189     },
19190     
19191     /*onSelectedChange : function(state){
19192         if(state){
19193             this.focus();
19194             this.addClass("x-tree-selected");
19195         }else{
19196             //this.blur();
19197             this.removeClass("x-tree-selected");
19198         }
19199     },*/
19200     addClass : function(cls){
19201         if(this.elRow){
19202             Roo.fly(this.elRow).addClass(cls);
19203         }
19204         
19205     },
19206     
19207     
19208     removeClass : function(cls){
19209         if(this.elRow){
19210             Roo.fly(this.elRow).removeClass(cls);
19211         }
19212     }
19213
19214     
19215     
19216 });//<Script type="text/javascript">
19217
19218 /*
19219  * Based on:
19220  * Ext JS Library 1.1.1
19221  * Copyright(c) 2006-2007, Ext JS, LLC.
19222  *
19223  * Originally Released Under LGPL - original licence link has changed is not relivant.
19224  *
19225  * Fork - LGPL
19226  * <script type="text/javascript">
19227  */
19228  
19229
19230 /**
19231  * @class Roo.tree.ColumnTree
19232  * @extends Roo.data.TreePanel
19233  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19234  * @cfg {int} borderWidth  compined right/left border allowance
19235  * @constructor
19236  * @param {String/HTMLElement/Element} el The container element
19237  * @param {Object} config
19238  */
19239 Roo.tree.ColumnTree =  function(el, config)
19240 {
19241    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19242    this.addEvents({
19243         /**
19244         * @event resize
19245         * Fire this event on a container when it resizes
19246         * @param {int} w Width
19247         * @param {int} h Height
19248         */
19249        "resize" : true
19250     });
19251     this.on('resize', this.onResize, this);
19252 };
19253
19254 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19255     //lines:false,
19256     
19257     
19258     borderWidth: Roo.isBorderBox ? 0 : 2, 
19259     headEls : false,
19260     
19261     render : function(){
19262         // add the header.....
19263        
19264         Roo.tree.ColumnTree.superclass.render.apply(this);
19265         
19266         this.el.addClass('x-column-tree');
19267         
19268         this.headers = this.el.createChild(
19269             {cls:'x-tree-headers'},this.innerCt.dom);
19270    
19271         var cols = this.columns, c;
19272         var totalWidth = 0;
19273         this.headEls = [];
19274         var  len = cols.length;
19275         for(var i = 0; i < len; i++){
19276              c = cols[i];
19277              totalWidth += c.width;
19278             this.headEls.push(this.headers.createChild({
19279                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19280                  cn: {
19281                      cls:'x-tree-hd-text',
19282                      html: c.header
19283                  },
19284                  style:'width:'+(c.width-this.borderWidth)+'px;'
19285              }));
19286         }
19287         this.headers.createChild({cls:'x-clear'});
19288         // prevent floats from wrapping when clipped
19289         this.headers.setWidth(totalWidth);
19290         //this.innerCt.setWidth(totalWidth);
19291         this.innerCt.setStyle({ overflow: 'auto' });
19292         this.onResize(this.width, this.height);
19293              
19294         
19295     },
19296     onResize : function(w,h)
19297     {
19298         this.height = h;
19299         this.width = w;
19300         // resize cols..
19301         this.innerCt.setWidth(this.width);
19302         this.innerCt.setHeight(this.height-20);
19303         
19304         // headers...
19305         var cols = this.columns, c;
19306         var totalWidth = 0;
19307         var expEl = false;
19308         var len = cols.length;
19309         for(var i = 0; i < len; i++){
19310             c = cols[i];
19311             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19312                 // it's the expander..
19313                 expEl  = this.headEls[i];
19314                 continue;
19315             }
19316             totalWidth += c.width;
19317             
19318         }
19319         if (expEl) {
19320             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19321         }
19322         this.headers.setWidth(w-20);
19323
19324         
19325         
19326         
19327     }
19328 });
19329 /*
19330  * Based on:
19331  * Ext JS Library 1.1.1
19332  * Copyright(c) 2006-2007, Ext JS, LLC.
19333  *
19334  * Originally Released Under LGPL - original licence link has changed is not relivant.
19335  *
19336  * Fork - LGPL
19337  * <script type="text/javascript">
19338  */
19339  
19340 /**
19341  * @class Roo.menu.Menu
19342  * @extends Roo.util.Observable
19343  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19344  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19345  * @constructor
19346  * Creates a new Menu
19347  * @param {Object} config Configuration options
19348  */
19349 Roo.menu.Menu = function(config){
19350     Roo.apply(this, config);
19351     this.id = this.id || Roo.id();
19352     this.addEvents({
19353         /**
19354          * @event beforeshow
19355          * Fires before this menu is displayed
19356          * @param {Roo.menu.Menu} this
19357          */
19358         beforeshow : true,
19359         /**
19360          * @event beforehide
19361          * Fires before this menu is hidden
19362          * @param {Roo.menu.Menu} this
19363          */
19364         beforehide : true,
19365         /**
19366          * @event show
19367          * Fires after this menu is displayed
19368          * @param {Roo.menu.Menu} this
19369          */
19370         show : true,
19371         /**
19372          * @event hide
19373          * Fires after this menu is hidden
19374          * @param {Roo.menu.Menu} this
19375          */
19376         hide : true,
19377         /**
19378          * @event click
19379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19380          * @param {Roo.menu.Menu} this
19381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19382          * @param {Roo.EventObject} e
19383          */
19384         click : true,
19385         /**
19386          * @event mouseover
19387          * Fires when the mouse is hovering over this menu
19388          * @param {Roo.menu.Menu} this
19389          * @param {Roo.EventObject} e
19390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19391          */
19392         mouseover : true,
19393         /**
19394          * @event mouseout
19395          * Fires when the mouse exits this menu
19396          * @param {Roo.menu.Menu} this
19397          * @param {Roo.EventObject} e
19398          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19399          */
19400         mouseout : true,
19401         /**
19402          * @event itemclick
19403          * Fires when a menu item contained in this menu is clicked
19404          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19405          * @param {Roo.EventObject} e
19406          */
19407         itemclick: true
19408     });
19409     if (this.registerMenu) {
19410         Roo.menu.MenuMgr.register(this);
19411     }
19412     
19413     var mis = this.items;
19414     this.items = new Roo.util.MixedCollection();
19415     if(mis){
19416         this.add.apply(this, mis);
19417     }
19418 };
19419
19420 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19421     /**
19422      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19423      */
19424     minWidth : 120,
19425     /**
19426      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19427      * for bottom-right shadow (defaults to "sides")
19428      */
19429     shadow : "sides",
19430     /**
19431      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19432      * this menu (defaults to "tl-tr?")
19433      */
19434     subMenuAlign : "tl-tr?",
19435     /**
19436      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19437      * relative to its element of origin (defaults to "tl-bl?")
19438      */
19439     defaultAlign : "tl-bl?",
19440     /**
19441      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19442      */
19443     allowOtherMenus : false,
19444     /**
19445      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19446      */
19447     registerMenu : true,
19448
19449     hidden:true,
19450
19451     // private
19452     render : function(){
19453         if(this.el){
19454             return;
19455         }
19456         var el = this.el = new Roo.Layer({
19457             cls: "x-menu",
19458             shadow:this.shadow,
19459             constrain: false,
19460             parentEl: this.parentEl || document.body,
19461             zindex:15000
19462         });
19463
19464         this.keyNav = new Roo.menu.MenuNav(this);
19465
19466         if(this.plain){
19467             el.addClass("x-menu-plain");
19468         }
19469         if(this.cls){
19470             el.addClass(this.cls);
19471         }
19472         // generic focus element
19473         this.focusEl = el.createChild({
19474             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19475         });
19476         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19477         ul.on("click", this.onClick, this);
19478         ul.on("mouseover", this.onMouseOver, this);
19479         ul.on("mouseout", this.onMouseOut, this);
19480         this.items.each(function(item){
19481             var li = document.createElement("li");
19482             li.className = "x-menu-list-item";
19483             ul.dom.appendChild(li);
19484             item.render(li, this);
19485         }, this);
19486         this.ul = ul;
19487         this.autoWidth();
19488     },
19489
19490     // private
19491     autoWidth : function(){
19492         var el = this.el, ul = this.ul;
19493         if(!el){
19494             return;
19495         }
19496         var w = this.width;
19497         if(w){
19498             el.setWidth(w);
19499         }else if(Roo.isIE){
19500             el.setWidth(this.minWidth);
19501             var t = el.dom.offsetWidth; // force recalc
19502             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19503         }
19504     },
19505
19506     // private
19507     delayAutoWidth : function(){
19508         if(this.rendered){
19509             if(!this.awTask){
19510                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19511             }
19512             this.awTask.delay(20);
19513         }
19514     },
19515
19516     // private
19517     findTargetItem : function(e){
19518         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19519         if(t && t.menuItemId){
19520             return this.items.get(t.menuItemId);
19521         }
19522     },
19523
19524     // private
19525     onClick : function(e){
19526         var t;
19527         if(t = this.findTargetItem(e)){
19528             t.onClick(e);
19529             this.fireEvent("click", this, t, e);
19530         }
19531     },
19532
19533     // private
19534     setActiveItem : function(item, autoExpand){
19535         if(item != this.activeItem){
19536             if(this.activeItem){
19537                 this.activeItem.deactivate();
19538             }
19539             this.activeItem = item;
19540             item.activate(autoExpand);
19541         }else if(autoExpand){
19542             item.expandMenu();
19543         }
19544     },
19545
19546     // private
19547     tryActivate : function(start, step){
19548         var items = this.items;
19549         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19550             var item = items.get(i);
19551             if(!item.disabled && item.canActivate){
19552                 this.setActiveItem(item, false);
19553                 return item;
19554             }
19555         }
19556         return false;
19557     },
19558
19559     // private
19560     onMouseOver : function(e){
19561         var t;
19562         if(t = this.findTargetItem(e)){
19563             if(t.canActivate && !t.disabled){
19564                 this.setActiveItem(t, true);
19565             }
19566         }
19567         this.fireEvent("mouseover", this, e, t);
19568     },
19569
19570     // private
19571     onMouseOut : function(e){
19572         var t;
19573         if(t = this.findTargetItem(e)){
19574             if(t == this.activeItem && t.shouldDeactivate(e)){
19575                 this.activeItem.deactivate();
19576                 delete this.activeItem;
19577             }
19578         }
19579         this.fireEvent("mouseout", this, e, t);
19580     },
19581
19582     /**
19583      * Read-only.  Returns true if the menu is currently displayed, else false.
19584      * @type Boolean
19585      */
19586     isVisible : function(){
19587         return this.el && !this.hidden;
19588     },
19589
19590     /**
19591      * Displays this menu relative to another element
19592      * @param {String/HTMLElement/Roo.Element} element The element to align to
19593      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19594      * the element (defaults to this.defaultAlign)
19595      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19596      */
19597     show : function(el, pos, parentMenu){
19598         this.parentMenu = parentMenu;
19599         if(!this.el){
19600             this.render();
19601         }
19602         this.fireEvent("beforeshow", this);
19603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19604     },
19605
19606     /**
19607      * Displays this menu at a specific xy position
19608      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19609      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19610      */
19611     showAt : function(xy, parentMenu, /* private: */_e){
19612         this.parentMenu = parentMenu;
19613         if(!this.el){
19614             this.render();
19615         }
19616         if(_e !== false){
19617             this.fireEvent("beforeshow", this);
19618             xy = this.el.adjustForConstraints(xy);
19619         }
19620         this.el.setXY(xy);
19621         this.el.show();
19622         this.hidden = false;
19623         this.focus();
19624         this.fireEvent("show", this);
19625     },
19626
19627     focus : function(){
19628         if(!this.hidden){
19629             this.doFocus.defer(50, this);
19630         }
19631     },
19632
19633     doFocus : function(){
19634         if(!this.hidden){
19635             this.focusEl.focus();
19636         }
19637     },
19638
19639     /**
19640      * Hides this menu and optionally all parent menus
19641      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19642      */
19643     hide : function(deep){
19644         if(this.el && this.isVisible()){
19645             this.fireEvent("beforehide", this);
19646             if(this.activeItem){
19647                 this.activeItem.deactivate();
19648                 this.activeItem = null;
19649             }
19650             this.el.hide();
19651             this.hidden = true;
19652             this.fireEvent("hide", this);
19653         }
19654         if(deep === true && this.parentMenu){
19655             this.parentMenu.hide(true);
19656         }
19657     },
19658
19659     /**
19660      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19661      * Any of the following are valid:
19662      * <ul>
19663      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19664      * <li>An HTMLElement object which will be converted to a menu item</li>
19665      * <li>A menu item config object that will be created as a new menu item</li>
19666      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19667      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19668      * </ul>
19669      * Usage:
19670      * <pre><code>
19671 // Create the menu
19672 var menu = new Roo.menu.Menu();
19673
19674 // Create a menu item to add by reference
19675 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19676
19677 // Add a bunch of items at once using different methods.
19678 // Only the last item added will be returned.
19679 var item = menu.add(
19680     menuItem,                // add existing item by ref
19681     'Dynamic Item',          // new TextItem
19682     '-',                     // new separator
19683     { text: 'Config Item' }  // new item by config
19684 );
19685 </code></pre>
19686      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19687      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19688      */
19689     add : function(){
19690         var a = arguments, l = a.length, item;
19691         for(var i = 0; i < l; i++){
19692             var el = a[i];
19693             if ((typeof(el) == "object") && el.xtype && el.xns) {
19694                 el = Roo.factory(el, Roo.menu);
19695             }
19696             
19697             if(el.render){ // some kind of Item
19698                 item = this.addItem(el);
19699             }else if(typeof el == "string"){ // string
19700                 if(el == "separator" || el == "-"){
19701                     item = this.addSeparator();
19702                 }else{
19703                     item = this.addText(el);
19704                 }
19705             }else if(el.tagName || el.el){ // element
19706                 item = this.addElement(el);
19707             }else if(typeof el == "object"){ // must be menu item config?
19708                 item = this.addMenuItem(el);
19709             }
19710         }
19711         return item;
19712     },
19713
19714     /**
19715      * Returns this menu's underlying {@link Roo.Element} object
19716      * @return {Roo.Element} The element
19717      */
19718     getEl : function(){
19719         if(!this.el){
19720             this.render();
19721         }
19722         return this.el;
19723     },
19724
19725     /**
19726      * Adds a separator bar to the menu
19727      * @return {Roo.menu.Item} The menu item that was added
19728      */
19729     addSeparator : function(){
19730         return this.addItem(new Roo.menu.Separator());
19731     },
19732
19733     /**
19734      * Adds an {@link Roo.Element} object to the menu
19735      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19736      * @return {Roo.menu.Item} The menu item that was added
19737      */
19738     addElement : function(el){
19739         return this.addItem(new Roo.menu.BaseItem(el));
19740     },
19741
19742     /**
19743      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19744      * @param {Roo.menu.Item} item The menu item to add
19745      * @return {Roo.menu.Item} The menu item that was added
19746      */
19747     addItem : function(item){
19748         this.items.add(item);
19749         if(this.ul){
19750             var li = document.createElement("li");
19751             li.className = "x-menu-list-item";
19752             this.ul.dom.appendChild(li);
19753             item.render(li, this);
19754             this.delayAutoWidth();
19755         }
19756         return item;
19757     },
19758
19759     /**
19760      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19761      * @param {Object} config A MenuItem config object
19762      * @return {Roo.menu.Item} The menu item that was added
19763      */
19764     addMenuItem : function(config){
19765         if(!(config instanceof Roo.menu.Item)){
19766             if(typeof config.checked == "boolean"){ // must be check menu item config?
19767                 config = new Roo.menu.CheckItem(config);
19768             }else{
19769                 config = new Roo.menu.Item(config);
19770             }
19771         }
19772         return this.addItem(config);
19773     },
19774
19775     /**
19776      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19777      * @param {String} text The text to display in the menu item
19778      * @return {Roo.menu.Item} The menu item that was added
19779      */
19780     addText : function(text){
19781         return this.addItem(new Roo.menu.TextItem({ text : text }));
19782     },
19783
19784     /**
19785      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19786      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19787      * @param {Roo.menu.Item} item The menu item to add
19788      * @return {Roo.menu.Item} The menu item that was added
19789      */
19790     insert : function(index, item){
19791         this.items.insert(index, item);
19792         if(this.ul){
19793             var li = document.createElement("li");
19794             li.className = "x-menu-list-item";
19795             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19796             item.render(li, this);
19797             this.delayAutoWidth();
19798         }
19799         return item;
19800     },
19801
19802     /**
19803      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19804      * @param {Roo.menu.Item} item The menu item to remove
19805      */
19806     remove : function(item){
19807         this.items.removeKey(item.id);
19808         item.destroy();
19809     },
19810
19811     /**
19812      * Removes and destroys all items in the menu
19813      */
19814     removeAll : function(){
19815         var f;
19816         while(f = this.items.first()){
19817             this.remove(f);
19818         }
19819     }
19820 });
19821
19822 // MenuNav is a private utility class used internally by the Menu
19823 Roo.menu.MenuNav = function(menu){
19824     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19825     this.scope = this.menu = menu;
19826 };
19827
19828 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19829     doRelay : function(e, h){
19830         var k = e.getKey();
19831         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19832             this.menu.tryActivate(0, 1);
19833             return false;
19834         }
19835         return h.call(this.scope || this, e, this.menu);
19836     },
19837
19838     up : function(e, m){
19839         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19840             m.tryActivate(m.items.length-1, -1);
19841         }
19842     },
19843
19844     down : function(e, m){
19845         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19846             m.tryActivate(0, 1);
19847         }
19848     },
19849
19850     right : function(e, m){
19851         if(m.activeItem){
19852             m.activeItem.expandMenu(true);
19853         }
19854     },
19855
19856     left : function(e, m){
19857         m.hide();
19858         if(m.parentMenu && m.parentMenu.activeItem){
19859             m.parentMenu.activeItem.activate();
19860         }
19861     },
19862
19863     enter : function(e, m){
19864         if(m.activeItem){
19865             e.stopPropagation();
19866             m.activeItem.onClick(e);
19867             m.fireEvent("click", this, m.activeItem);
19868             return true;
19869         }
19870     }
19871 });/*
19872  * Based on:
19873  * Ext JS Library 1.1.1
19874  * Copyright(c) 2006-2007, Ext JS, LLC.
19875  *
19876  * Originally Released Under LGPL - original licence link has changed is not relivant.
19877  *
19878  * Fork - LGPL
19879  * <script type="text/javascript">
19880  */
19881  
19882 /**
19883  * @class Roo.menu.MenuMgr
19884  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19885  * @singleton
19886  */
19887 Roo.menu.MenuMgr = function(){
19888    var menus, active, groups = {}, attached = false, lastShow = new Date();
19889
19890    // private - called when first menu is created
19891    function init(){
19892        menus = {};
19893        active = new Roo.util.MixedCollection();
19894        Roo.get(document).addKeyListener(27, function(){
19895            if(active.length > 0){
19896                hideAll();
19897            }
19898        });
19899    }
19900
19901    // private
19902    function hideAll(){
19903        if(active && active.length > 0){
19904            var c = active.clone();
19905            c.each(function(m){
19906                m.hide();
19907            });
19908        }
19909    }
19910
19911    // private
19912    function onHide(m){
19913        active.remove(m);
19914        if(active.length < 1){
19915            Roo.get(document).un("mousedown", onMouseDown);
19916            attached = false;
19917        }
19918    }
19919
19920    // private
19921    function onShow(m){
19922        var last = active.last();
19923        lastShow = new Date();
19924        active.add(m);
19925        if(!attached){
19926            Roo.get(document).on("mousedown", onMouseDown);
19927            attached = true;
19928        }
19929        if(m.parentMenu){
19930           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19931           m.parentMenu.activeChild = m;
19932        }else if(last && last.isVisible()){
19933           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19934        }
19935    }
19936
19937    // private
19938    function onBeforeHide(m){
19939        if(m.activeChild){
19940            m.activeChild.hide();
19941        }
19942        if(m.autoHideTimer){
19943            clearTimeout(m.autoHideTimer);
19944            delete m.autoHideTimer;
19945        }
19946    }
19947
19948    // private
19949    function onBeforeShow(m){
19950        var pm = m.parentMenu;
19951        if(!pm && !m.allowOtherMenus){
19952            hideAll();
19953        }else if(pm && pm.activeChild && active != m){
19954            pm.activeChild.hide();
19955        }
19956    }
19957
19958    // private
19959    function onMouseDown(e){
19960        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19961            hideAll();
19962        }
19963    }
19964
19965    // private
19966    function onBeforeCheck(mi, state){
19967        if(state){
19968            var g = groups[mi.group];
19969            for(var i = 0, l = g.length; i < l; i++){
19970                if(g[i] != mi){
19971                    g[i].setChecked(false);
19972                }
19973            }
19974        }
19975    }
19976
19977    return {
19978
19979        /**
19980         * Hides all menus that are currently visible
19981         */
19982        hideAll : function(){
19983             hideAll();  
19984        },
19985
19986        // private
19987        register : function(menu){
19988            if(!menus){
19989                init();
19990            }
19991            menus[menu.id] = menu;
19992            menu.on("beforehide", onBeforeHide);
19993            menu.on("hide", onHide);
19994            menu.on("beforeshow", onBeforeShow);
19995            menu.on("show", onShow);
19996            var g = menu.group;
19997            if(g && menu.events["checkchange"]){
19998                if(!groups[g]){
19999                    groups[g] = [];
20000                }
20001                groups[g].push(menu);
20002                menu.on("checkchange", onCheck);
20003            }
20004        },
20005
20006         /**
20007          * Returns a {@link Roo.menu.Menu} object
20008          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20009          * be used to generate and return a new Menu instance.
20010          */
20011        get : function(menu){
20012            if(typeof menu == "string"){ // menu id
20013                return menus[menu];
20014            }else if(menu.events){  // menu instance
20015                return menu;
20016            }else if(typeof menu.length == 'number'){ // array of menu items?
20017                return new Roo.menu.Menu({items:menu});
20018            }else{ // otherwise, must be a config
20019                return new Roo.menu.Menu(menu);
20020            }
20021        },
20022
20023        // private
20024        unregister : function(menu){
20025            delete menus[menu.id];
20026            menu.un("beforehide", onBeforeHide);
20027            menu.un("hide", onHide);
20028            menu.un("beforeshow", onBeforeShow);
20029            menu.un("show", onShow);
20030            var g = menu.group;
20031            if(g && menu.events["checkchange"]){
20032                groups[g].remove(menu);
20033                menu.un("checkchange", onCheck);
20034            }
20035        },
20036
20037        // private
20038        registerCheckable : function(menuItem){
20039            var g = menuItem.group;
20040            if(g){
20041                if(!groups[g]){
20042                    groups[g] = [];
20043                }
20044                groups[g].push(menuItem);
20045                menuItem.on("beforecheckchange", onBeforeCheck);
20046            }
20047        },
20048
20049        // private
20050        unregisterCheckable : function(menuItem){
20051            var g = menuItem.group;
20052            if(g){
20053                groups[g].remove(menuItem);
20054                menuItem.un("beforecheckchange", onBeforeCheck);
20055            }
20056        }
20057    };
20058 }();/*
20059  * Based on:
20060  * Ext JS Library 1.1.1
20061  * Copyright(c) 2006-2007, Ext JS, LLC.
20062  *
20063  * Originally Released Under LGPL - original licence link has changed is not relivant.
20064  *
20065  * Fork - LGPL
20066  * <script type="text/javascript">
20067  */
20068  
20069
20070 /**
20071  * @class Roo.menu.BaseItem
20072  * @extends Roo.Component
20073  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20074  * management and base configuration options shared by all menu components.
20075  * @constructor
20076  * Creates a new BaseItem
20077  * @param {Object} config Configuration options
20078  */
20079 Roo.menu.BaseItem = function(config){
20080     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20081
20082     this.addEvents({
20083         /**
20084          * @event click
20085          * Fires when this item is clicked
20086          * @param {Roo.menu.BaseItem} this
20087          * @param {Roo.EventObject} e
20088          */
20089         click: true,
20090         /**
20091          * @event activate
20092          * Fires when this item is activated
20093          * @param {Roo.menu.BaseItem} this
20094          */
20095         activate : true,
20096         /**
20097          * @event deactivate
20098          * Fires when this item is deactivated
20099          * @param {Roo.menu.BaseItem} this
20100          */
20101         deactivate : true
20102     });
20103
20104     if(this.handler){
20105         this.on("click", this.handler, this.scope, true);
20106     }
20107 };
20108
20109 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20110     /**
20111      * @cfg {Function} handler
20112      * A function that will handle the click event of this menu item (defaults to undefined)
20113      */
20114     /**
20115      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20116      */
20117     canActivate : false,
20118     /**
20119      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20120      */
20121     activeClass : "x-menu-item-active",
20122     /**
20123      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20124      */
20125     hideOnClick : true,
20126     /**
20127      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20128      */
20129     hideDelay : 100,
20130
20131     // private
20132     ctype: "Roo.menu.BaseItem",
20133
20134     // private
20135     actionMode : "container",
20136
20137     // private
20138     render : function(container, parentMenu){
20139         this.parentMenu = parentMenu;
20140         Roo.menu.BaseItem.superclass.render.call(this, container);
20141         this.container.menuItemId = this.id;
20142     },
20143
20144     // private
20145     onRender : function(container, position){
20146         this.el = Roo.get(this.el);
20147         container.dom.appendChild(this.el.dom);
20148     },
20149
20150     // private
20151     onClick : function(e){
20152         if(!this.disabled && this.fireEvent("click", this, e) !== false
20153                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20154             this.handleClick(e);
20155         }else{
20156             e.stopEvent();
20157         }
20158     },
20159
20160     // private
20161     activate : function(){
20162         if(this.disabled){
20163             return false;
20164         }
20165         var li = this.container;
20166         li.addClass(this.activeClass);
20167         this.region = li.getRegion().adjust(2, 2, -2, -2);
20168         this.fireEvent("activate", this);
20169         return true;
20170     },
20171
20172     // private
20173     deactivate : function(){
20174         this.container.removeClass(this.activeClass);
20175         this.fireEvent("deactivate", this);
20176     },
20177
20178     // private
20179     shouldDeactivate : function(e){
20180         return !this.region || !this.region.contains(e.getPoint());
20181     },
20182
20183     // private
20184     handleClick : function(e){
20185         if(this.hideOnClick){
20186             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20187         }
20188     },
20189
20190     // private
20191     expandMenu : function(autoActivate){
20192         // do nothing
20193     },
20194
20195     // private
20196     hideMenu : function(){
20197         // do nothing
20198     }
20199 });/*
20200  * Based on:
20201  * Ext JS Library 1.1.1
20202  * Copyright(c) 2006-2007, Ext JS, LLC.
20203  *
20204  * Originally Released Under LGPL - original licence link has changed is not relivant.
20205  *
20206  * Fork - LGPL
20207  * <script type="text/javascript">
20208  */
20209  
20210 /**
20211  * @class Roo.menu.Adapter
20212  * @extends Roo.menu.BaseItem
20213  * 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.
20214  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20215  * @constructor
20216  * Creates a new Adapter
20217  * @param {Object} config Configuration options
20218  */
20219 Roo.menu.Adapter = function(component, config){
20220     Roo.menu.Adapter.superclass.constructor.call(this, config);
20221     this.component = component;
20222 };
20223 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20224     // private
20225     canActivate : true,
20226
20227     // private
20228     onRender : function(container, position){
20229         this.component.render(container);
20230         this.el = this.component.getEl();
20231     },
20232
20233     // private
20234     activate : function(){
20235         if(this.disabled){
20236             return false;
20237         }
20238         this.component.focus();
20239         this.fireEvent("activate", this);
20240         return true;
20241     },
20242
20243     // private
20244     deactivate : function(){
20245         this.fireEvent("deactivate", this);
20246     },
20247
20248     // private
20249     disable : function(){
20250         this.component.disable();
20251         Roo.menu.Adapter.superclass.disable.call(this);
20252     },
20253
20254     // private
20255     enable : function(){
20256         this.component.enable();
20257         Roo.menu.Adapter.superclass.enable.call(this);
20258     }
20259 });/*
20260  * Based on:
20261  * Ext JS Library 1.1.1
20262  * Copyright(c) 2006-2007, Ext JS, LLC.
20263  *
20264  * Originally Released Under LGPL - original licence link has changed is not relivant.
20265  *
20266  * Fork - LGPL
20267  * <script type="text/javascript">
20268  */
20269
20270 /**
20271  * @class Roo.menu.TextItem
20272  * @extends Roo.menu.BaseItem
20273  * Adds a static text string to a menu, usually used as either a heading or group separator.
20274  * Note: old style constructor with text is still supported.
20275  * 
20276  * @constructor
20277  * Creates a new TextItem
20278  * @param {Object} cfg Configuration
20279  */
20280 Roo.menu.TextItem = function(cfg){
20281     if (typeof(cfg) == 'string') {
20282         this.text = cfg;
20283     } else {
20284         Roo.apply(this,cfg);
20285     }
20286     
20287     Roo.menu.TextItem.superclass.constructor.call(this);
20288 };
20289
20290 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20291     /**
20292      * @cfg {Boolean} text Text to show on item.
20293      */
20294     text : '',
20295     
20296     /**
20297      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20298      */
20299     hideOnClick : false,
20300     /**
20301      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20302      */
20303     itemCls : "x-menu-text",
20304
20305     // private
20306     onRender : function(){
20307         var s = document.createElement("span");
20308         s.className = this.itemCls;
20309         s.innerHTML = this.text;
20310         this.el = s;
20311         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20312     }
20313 });/*
20314  * Based on:
20315  * Ext JS Library 1.1.1
20316  * Copyright(c) 2006-2007, Ext JS, LLC.
20317  *
20318  * Originally Released Under LGPL - original licence link has changed is not relivant.
20319  *
20320  * Fork - LGPL
20321  * <script type="text/javascript">
20322  */
20323
20324 /**
20325  * @class Roo.menu.Separator
20326  * @extends Roo.menu.BaseItem
20327  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20328  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20329  * @constructor
20330  * @param {Object} config Configuration options
20331  */
20332 Roo.menu.Separator = function(config){
20333     Roo.menu.Separator.superclass.constructor.call(this, config);
20334 };
20335
20336 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20337     /**
20338      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20339      */
20340     itemCls : "x-menu-sep",
20341     /**
20342      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20343      */
20344     hideOnClick : false,
20345
20346     // private
20347     onRender : function(li){
20348         var s = document.createElement("span");
20349         s.className = this.itemCls;
20350         s.innerHTML = "&#160;";
20351         this.el = s;
20352         li.addClass("x-menu-sep-li");
20353         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20354     }
20355 });/*
20356  * Based on:
20357  * Ext JS Library 1.1.1
20358  * Copyright(c) 2006-2007, Ext JS, LLC.
20359  *
20360  * Originally Released Under LGPL - original licence link has changed is not relivant.
20361  *
20362  * Fork - LGPL
20363  * <script type="text/javascript">
20364  */
20365 /**
20366  * @class Roo.menu.Item
20367  * @extends Roo.menu.BaseItem
20368  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20369  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20370  * activation and click handling.
20371  * @constructor
20372  * Creates a new Item
20373  * @param {Object} config Configuration options
20374  */
20375 Roo.menu.Item = function(config){
20376     Roo.menu.Item.superclass.constructor.call(this, config);
20377     if(this.menu){
20378         this.menu = Roo.menu.MenuMgr.get(this.menu);
20379     }
20380 };
20381 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20382     
20383     /**
20384      * @cfg {String} text
20385      * The text to show on the menu item.
20386      */
20387     text: '',
20388      /**
20389      * @cfg {String} HTML to render in menu
20390      * The text to show on the menu item (HTML version).
20391      */
20392     html: '',
20393     /**
20394      * @cfg {String} icon
20395      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20396      */
20397     icon: undefined,
20398     /**
20399      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20400      */
20401     itemCls : "x-menu-item",
20402     /**
20403      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20404      */
20405     canActivate : true,
20406     /**
20407      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20408      */
20409     showDelay: 200,
20410     // doc'd in BaseItem
20411     hideDelay: 200,
20412
20413     // private
20414     ctype: "Roo.menu.Item",
20415     
20416     // private
20417     onRender : function(container, position){
20418         var el = document.createElement("a");
20419         el.hideFocus = true;
20420         el.unselectable = "on";
20421         el.href = this.href || "#";
20422         if(this.hrefTarget){
20423             el.target = this.hrefTarget;
20424         }
20425         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20426         
20427         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20428         
20429         el.innerHTML = String.format(
20430                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20431                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20432         this.el = el;
20433         Roo.menu.Item.superclass.onRender.call(this, container, position);
20434     },
20435
20436     /**
20437      * Sets the text to display in this menu item
20438      * @param {String} text The text to display
20439      * @param {Boolean} isHTML true to indicate text is pure html.
20440      */
20441     setText : function(text, isHTML){
20442         if (isHTML) {
20443             this.html = text;
20444         } else {
20445             this.text = text;
20446             this.html = '';
20447         }
20448         if(this.rendered){
20449             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20450      
20451             this.el.update(String.format(
20452                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20453                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20454             this.parentMenu.autoWidth();
20455         }
20456     },
20457
20458     // private
20459     handleClick : function(e){
20460         if(!this.href){ // if no link defined, stop the event automatically
20461             e.stopEvent();
20462         }
20463         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20464     },
20465
20466     // private
20467     activate : function(autoExpand){
20468         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20469             this.focus();
20470             if(autoExpand){
20471                 this.expandMenu();
20472             }
20473         }
20474         return true;
20475     },
20476
20477     // private
20478     shouldDeactivate : function(e){
20479         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20480             if(this.menu && this.menu.isVisible()){
20481                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20482             }
20483             return true;
20484         }
20485         return false;
20486     },
20487
20488     // private
20489     deactivate : function(){
20490         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20491         this.hideMenu();
20492     },
20493
20494     // private
20495     expandMenu : function(autoActivate){
20496         if(!this.disabled && this.menu){
20497             clearTimeout(this.hideTimer);
20498             delete this.hideTimer;
20499             if(!this.menu.isVisible() && !this.showTimer){
20500                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20501             }else if (this.menu.isVisible() && autoActivate){
20502                 this.menu.tryActivate(0, 1);
20503             }
20504         }
20505     },
20506
20507     // private
20508     deferExpand : function(autoActivate){
20509         delete this.showTimer;
20510         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20511         if(autoActivate){
20512             this.menu.tryActivate(0, 1);
20513         }
20514     },
20515
20516     // private
20517     hideMenu : function(){
20518         clearTimeout(this.showTimer);
20519         delete this.showTimer;
20520         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20521             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20522         }
20523     },
20524
20525     // private
20526     deferHide : function(){
20527         delete this.hideTimer;
20528         this.menu.hide();
20529     }
20530 });/*
20531  * Based on:
20532  * Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  *
20535  * Originally Released Under LGPL - original licence link has changed is not relivant.
20536  *
20537  * Fork - LGPL
20538  * <script type="text/javascript">
20539  */
20540  
20541 /**
20542  * @class Roo.menu.CheckItem
20543  * @extends Roo.menu.Item
20544  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20545  * @constructor
20546  * Creates a new CheckItem
20547  * @param {Object} config Configuration options
20548  */
20549 Roo.menu.CheckItem = function(config){
20550     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20551     this.addEvents({
20552         /**
20553          * @event beforecheckchange
20554          * Fires before the checked value is set, providing an opportunity to cancel if needed
20555          * @param {Roo.menu.CheckItem} this
20556          * @param {Boolean} checked The new checked value that will be set
20557          */
20558         "beforecheckchange" : true,
20559         /**
20560          * @event checkchange
20561          * Fires after the checked value has been set
20562          * @param {Roo.menu.CheckItem} this
20563          * @param {Boolean} checked The checked value that was set
20564          */
20565         "checkchange" : true
20566     });
20567     if(this.checkHandler){
20568         this.on('checkchange', this.checkHandler, this.scope);
20569     }
20570 };
20571 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20572     /**
20573      * @cfg {String} group
20574      * All check items with the same group name will automatically be grouped into a single-select
20575      * radio button group (defaults to '')
20576      */
20577     /**
20578      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20579      */
20580     itemCls : "x-menu-item x-menu-check-item",
20581     /**
20582      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20583      */
20584     groupClass : "x-menu-group-item",
20585
20586     /**
20587      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20588      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20589      * initialized with checked = true will be rendered as checked.
20590      */
20591     checked: false,
20592
20593     // private
20594     ctype: "Roo.menu.CheckItem",
20595
20596     // private
20597     onRender : function(c){
20598         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20599         if(this.group){
20600             this.el.addClass(this.groupClass);
20601         }
20602         Roo.menu.MenuMgr.registerCheckable(this);
20603         if(this.checked){
20604             this.checked = false;
20605             this.setChecked(true, true);
20606         }
20607     },
20608
20609     // private
20610     destroy : function(){
20611         if(this.rendered){
20612             Roo.menu.MenuMgr.unregisterCheckable(this);
20613         }
20614         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20615     },
20616
20617     /**
20618      * Set the checked state of this item
20619      * @param {Boolean} checked The new checked value
20620      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20621      */
20622     setChecked : function(state, suppressEvent){
20623         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20624             if(this.container){
20625                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20626             }
20627             this.checked = state;
20628             if(suppressEvent !== true){
20629                 this.fireEvent("checkchange", this, state);
20630             }
20631         }
20632     },
20633
20634     // private
20635     handleClick : function(e){
20636        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20637            this.setChecked(!this.checked);
20638        }
20639        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20640     }
20641 });/*
20642  * Based on:
20643  * Ext JS Library 1.1.1
20644  * Copyright(c) 2006-2007, Ext JS, LLC.
20645  *
20646  * Originally Released Under LGPL - original licence link has changed is not relivant.
20647  *
20648  * Fork - LGPL
20649  * <script type="text/javascript">
20650  */
20651  
20652 /**
20653  * @class Roo.menu.DateItem
20654  * @extends Roo.menu.Adapter
20655  * A menu item that wraps the {@link Roo.DatPicker} component.
20656  * @constructor
20657  * Creates a new DateItem
20658  * @param {Object} config Configuration options
20659  */
20660 Roo.menu.DateItem = function(config){
20661     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20662     /** The Roo.DatePicker object @type Roo.DatePicker */
20663     this.picker = this.component;
20664     this.addEvents({select: true});
20665     
20666     this.picker.on("render", function(picker){
20667         picker.getEl().swallowEvent("click");
20668         picker.container.addClass("x-menu-date-item");
20669     });
20670
20671     this.picker.on("select", this.onSelect, this);
20672 };
20673
20674 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20675     // private
20676     onSelect : function(picker, date){
20677         this.fireEvent("select", this, date, picker);
20678         Roo.menu.DateItem.superclass.handleClick.call(this);
20679     }
20680 });/*
20681  * Based on:
20682  * Ext JS Library 1.1.1
20683  * Copyright(c) 2006-2007, Ext JS, LLC.
20684  *
20685  * Originally Released Under LGPL - original licence link has changed is not relivant.
20686  *
20687  * Fork - LGPL
20688  * <script type="text/javascript">
20689  */
20690  
20691 /**
20692  * @class Roo.menu.ColorItem
20693  * @extends Roo.menu.Adapter
20694  * A menu item that wraps the {@link Roo.ColorPalette} component.
20695  * @constructor
20696  * Creates a new ColorItem
20697  * @param {Object} config Configuration options
20698  */
20699 Roo.menu.ColorItem = function(config){
20700     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20701     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20702     this.palette = this.component;
20703     this.relayEvents(this.palette, ["select"]);
20704     if(this.selectHandler){
20705         this.on('select', this.selectHandler, this.scope);
20706     }
20707 };
20708 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20709  * Based on:
20710  * Ext JS Library 1.1.1
20711  * Copyright(c) 2006-2007, Ext JS, LLC.
20712  *
20713  * Originally Released Under LGPL - original licence link has changed is not relivant.
20714  *
20715  * Fork - LGPL
20716  * <script type="text/javascript">
20717  */
20718  
20719
20720 /**
20721  * @class Roo.menu.DateMenu
20722  * @extends Roo.menu.Menu
20723  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20724  * @constructor
20725  * Creates a new DateMenu
20726  * @param {Object} config Configuration options
20727  */
20728 Roo.menu.DateMenu = function(config){
20729     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20730     this.plain = true;
20731     var di = new Roo.menu.DateItem(config);
20732     this.add(di);
20733     /**
20734      * The {@link Roo.DatePicker} instance for this DateMenu
20735      * @type DatePicker
20736      */
20737     this.picker = di.picker;
20738     /**
20739      * @event select
20740      * @param {DatePicker} picker
20741      * @param {Date} date
20742      */
20743     this.relayEvents(di, ["select"]);
20744
20745     this.on('beforeshow', function(){
20746         if(this.picker){
20747             this.picker.hideMonthPicker(true);
20748         }
20749     }, this);
20750 };
20751 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20752     cls:'x-date-menu'
20753 });/*
20754  * Based on:
20755  * Ext JS Library 1.1.1
20756  * Copyright(c) 2006-2007, Ext JS, LLC.
20757  *
20758  * Originally Released Under LGPL - original licence link has changed is not relivant.
20759  *
20760  * Fork - LGPL
20761  * <script type="text/javascript">
20762  */
20763  
20764
20765 /**
20766  * @class Roo.menu.ColorMenu
20767  * @extends Roo.menu.Menu
20768  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20769  * @constructor
20770  * Creates a new ColorMenu
20771  * @param {Object} config Configuration options
20772  */
20773 Roo.menu.ColorMenu = function(config){
20774     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20775     this.plain = true;
20776     var ci = new Roo.menu.ColorItem(config);
20777     this.add(ci);
20778     /**
20779      * The {@link Roo.ColorPalette} instance for this ColorMenu
20780      * @type ColorPalette
20781      */
20782     this.palette = ci.palette;
20783     /**
20784      * @event select
20785      * @param {ColorPalette} palette
20786      * @param {String} color
20787      */
20788     this.relayEvents(ci, ["select"]);
20789 };
20790 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20791  * Based on:
20792  * Ext JS Library 1.1.1
20793  * Copyright(c) 2006-2007, Ext JS, LLC.
20794  *
20795  * Originally Released Under LGPL - original licence link has changed is not relivant.
20796  *
20797  * Fork - LGPL
20798  * <script type="text/javascript">
20799  */
20800  
20801 /**
20802  * @class Roo.form.Field
20803  * @extends Roo.BoxComponent
20804  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20805  * @constructor
20806  * Creates a new Field
20807  * @param {Object} config Configuration options
20808  */
20809 Roo.form.Field = function(config){
20810     Roo.form.Field.superclass.constructor.call(this, config);
20811 };
20812
20813 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20814     /**
20815      * @cfg {String} fieldLabel Label to use when rendering a form.
20816      */
20817        /**
20818      * @cfg {String} qtip Mouse over tip
20819      */
20820      
20821     /**
20822      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20823      */
20824     invalidClass : "x-form-invalid",
20825     /**
20826      * @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")
20827      */
20828     invalidText : "The value in this field is invalid",
20829     /**
20830      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20831      */
20832     focusClass : "x-form-focus",
20833     /**
20834      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20835       automatic validation (defaults to "keyup").
20836      */
20837     validationEvent : "keyup",
20838     /**
20839      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20840      */
20841     validateOnBlur : true,
20842     /**
20843      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20844      */
20845     validationDelay : 250,
20846     /**
20847      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20848      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20849      */
20850     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20851     /**
20852      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20853      */
20854     fieldClass : "x-form-field",
20855     /**
20856      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20857      *<pre>
20858 Value         Description
20859 -----------   ----------------------------------------------------------------------
20860 qtip          Display a quick tip when the user hovers over the field
20861 title         Display a default browser title attribute popup
20862 under         Add a block div beneath the field containing the error text
20863 side          Add an error icon to the right of the field with a popup on hover
20864 [element id]  Add the error text directly to the innerHTML of the specified element
20865 </pre>
20866      */
20867     msgTarget : 'qtip',
20868     /**
20869      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20870      */
20871     msgFx : 'normal',
20872
20873     /**
20874      * @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.
20875      */
20876     readOnly : false,
20877
20878     /**
20879      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20880      */
20881     disabled : false,
20882
20883     /**
20884      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20885      */
20886     inputType : undefined,
20887     
20888     /**
20889      * @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).
20890          */
20891         tabIndex : undefined,
20892         
20893     // private
20894     isFormField : true,
20895
20896     // private
20897     hasFocus : false,
20898     /**
20899      * @property {Roo.Element} fieldEl
20900      * Element Containing the rendered Field (with label etc.)
20901      */
20902     /**
20903      * @cfg {Mixed} value A value to initialize this field with.
20904      */
20905     value : undefined,
20906
20907     /**
20908      * @cfg {String} name The field's HTML name attribute.
20909      */
20910     /**
20911      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20912      */
20913
20914         // private ??
20915         initComponent : function(){
20916         Roo.form.Field.superclass.initComponent.call(this);
20917         this.addEvents({
20918             /**
20919              * @event focus
20920              * Fires when this field receives input focus.
20921              * @param {Roo.form.Field} this
20922              */
20923             focus : true,
20924             /**
20925              * @event blur
20926              * Fires when this field loses input focus.
20927              * @param {Roo.form.Field} this
20928              */
20929             blur : true,
20930             /**
20931              * @event specialkey
20932              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20933              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20934              * @param {Roo.form.Field} this
20935              * @param {Roo.EventObject} e The event object
20936              */
20937             specialkey : true,
20938             /**
20939              * @event change
20940              * Fires just before the field blurs if the field value has changed.
20941              * @param {Roo.form.Field} this
20942              * @param {Mixed} newValue The new value
20943              * @param {Mixed} oldValue The original value
20944              */
20945             change : true,
20946             /**
20947              * @event invalid
20948              * Fires after the field has been marked as invalid.
20949              * @param {Roo.form.Field} this
20950              * @param {String} msg The validation message
20951              */
20952             invalid : true,
20953             /**
20954              * @event valid
20955              * Fires after the field has been validated with no errors.
20956              * @param {Roo.form.Field} this
20957              */
20958             valid : true,
20959              /**
20960              * @event keyup
20961              * Fires after the key up
20962              * @param {Roo.form.Field} this
20963              * @param {Roo.EventObject}  e The event Object
20964              */
20965             keyup : true
20966         });
20967     },
20968
20969     /**
20970      * Returns the name attribute of the field if available
20971      * @return {String} name The field name
20972      */
20973     getName: function(){
20974          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20975     },
20976
20977     // private
20978     onRender : function(ct, position){
20979         Roo.form.Field.superclass.onRender.call(this, ct, position);
20980         if(!this.el){
20981             var cfg = this.getAutoCreate();
20982             if(!cfg.name){
20983                 cfg.name = this.name || this.id;
20984             }
20985             if(this.inputType){
20986                 cfg.type = this.inputType;
20987             }
20988             this.el = ct.createChild(cfg, position);
20989         }
20990         var type = this.el.dom.type;
20991         if(type){
20992             if(type == 'password'){
20993                 type = 'text';
20994             }
20995             this.el.addClass('x-form-'+type);
20996         }
20997         if(this.readOnly){
20998             this.el.dom.readOnly = true;
20999         }
21000         if(this.tabIndex !== undefined){
21001             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21002         }
21003
21004         this.el.addClass([this.fieldClass, this.cls]);
21005         this.initValue();
21006     },
21007
21008     /**
21009      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21010      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21011      * @return {Roo.form.Field} this
21012      */
21013     applyTo : function(target){
21014         this.allowDomMove = false;
21015         this.el = Roo.get(target);
21016         this.render(this.el.dom.parentNode);
21017         return this;
21018     },
21019
21020     // private
21021     initValue : function(){
21022         if(this.value !== undefined){
21023             this.setValue(this.value);
21024         }else if(this.el.dom.value.length > 0){
21025             this.setValue(this.el.dom.value);
21026         }
21027     },
21028
21029     /**
21030      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21031      */
21032     isDirty : function() {
21033         if(this.disabled) {
21034             return false;
21035         }
21036         return String(this.getValue()) !== String(this.originalValue);
21037     },
21038
21039     // private
21040     afterRender : function(){
21041         Roo.form.Field.superclass.afterRender.call(this);
21042         this.initEvents();
21043     },
21044
21045     // private
21046     fireKey : function(e){
21047         //Roo.log('field ' + e.getKey());
21048         if(e.isNavKeyPress()){
21049             this.fireEvent("specialkey", this, e);
21050         }
21051     },
21052
21053     /**
21054      * Resets the current field value to the originally loaded value and clears any validation messages
21055      */
21056     reset : function(){
21057         this.setValue(this.originalValue);
21058         this.clearInvalid();
21059     },
21060
21061     // private
21062     initEvents : function(){
21063         // safari killled keypress - so keydown is now used..
21064         this.el.on("keydown" , this.fireKey,  this);
21065         this.el.on("focus", this.onFocus,  this);
21066         this.el.on("blur", this.onBlur,  this);
21067         this.el.relayEvent('keyup', this);
21068
21069         // reference to original value for reset
21070         this.originalValue = this.getValue();
21071     },
21072
21073     // private
21074     onFocus : function(){
21075         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21076             this.el.addClass(this.focusClass);
21077         }
21078         if(!this.hasFocus){
21079             this.hasFocus = true;
21080             this.startValue = this.getValue();
21081             this.fireEvent("focus", this);
21082         }
21083     },
21084
21085     beforeBlur : Roo.emptyFn,
21086
21087     // private
21088     onBlur : function(){
21089         this.beforeBlur();
21090         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21091             this.el.removeClass(this.focusClass);
21092         }
21093         this.hasFocus = false;
21094         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21095             this.validate();
21096         }
21097         var v = this.getValue();
21098         if(String(v) !== String(this.startValue)){
21099             this.fireEvent('change', this, v, this.startValue);
21100         }
21101         this.fireEvent("blur", this);
21102     },
21103
21104     /**
21105      * Returns whether or not the field value is currently valid
21106      * @param {Boolean} preventMark True to disable marking the field invalid
21107      * @return {Boolean} True if the value is valid, else false
21108      */
21109     isValid : function(preventMark){
21110         if(this.disabled){
21111             return true;
21112         }
21113         var restore = this.preventMark;
21114         this.preventMark = preventMark === true;
21115         var v = this.validateValue(this.processValue(this.getRawValue()));
21116         this.preventMark = restore;
21117         return v;
21118     },
21119
21120     /**
21121      * Validates the field value
21122      * @return {Boolean} True if the value is valid, else false
21123      */
21124     validate : function(){
21125         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21126             this.clearInvalid();
21127             return true;
21128         }
21129         return false;
21130     },
21131
21132     processValue : function(value){
21133         return value;
21134     },
21135
21136     // private
21137     // Subclasses should provide the validation implementation by overriding this
21138     validateValue : function(value){
21139         return true;
21140     },
21141
21142     /**
21143      * Mark this field as invalid
21144      * @param {String} msg The validation message
21145      */
21146     markInvalid : function(msg){
21147         if(!this.rendered || this.preventMark){ // not rendered
21148             return;
21149         }
21150         this.el.addClass(this.invalidClass);
21151         msg = msg || this.invalidText;
21152         switch(this.msgTarget){
21153             case 'qtip':
21154                 this.el.dom.qtip = msg;
21155                 this.el.dom.qclass = 'x-form-invalid-tip';
21156                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21157                     Roo.QuickTips.enable();
21158                 }
21159                 break;
21160             case 'title':
21161                 this.el.dom.title = msg;
21162                 break;
21163             case 'under':
21164                 if(!this.errorEl){
21165                     var elp = this.el.findParent('.x-form-element', 5, true);
21166                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21167                     this.errorEl.setWidth(elp.getWidth(true)-20);
21168                 }
21169                 this.errorEl.update(msg);
21170                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21171                 break;
21172             case 'side':
21173                 if(!this.errorIcon){
21174                     var elp = this.el.findParent('.x-form-element', 5, true);
21175                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21176                 }
21177                 this.alignErrorIcon();
21178                 this.errorIcon.dom.qtip = msg;
21179                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21180                 this.errorIcon.show();
21181                 this.on('resize', this.alignErrorIcon, this);
21182                 break;
21183             default:
21184                 var t = Roo.getDom(this.msgTarget);
21185                 t.innerHTML = msg;
21186                 t.style.display = this.msgDisplay;
21187                 break;
21188         }
21189         this.fireEvent('invalid', this, msg);
21190     },
21191
21192     // private
21193     alignErrorIcon : function(){
21194         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21195     },
21196
21197     /**
21198      * Clear any invalid styles/messages for this field
21199      */
21200     clearInvalid : function(){
21201         if(!this.rendered || this.preventMark){ // not rendered
21202             return;
21203         }
21204         this.el.removeClass(this.invalidClass);
21205         switch(this.msgTarget){
21206             case 'qtip':
21207                 this.el.dom.qtip = '';
21208                 break;
21209             case 'title':
21210                 this.el.dom.title = '';
21211                 break;
21212             case 'under':
21213                 if(this.errorEl){
21214                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21215                 }
21216                 break;
21217             case 'side':
21218                 if(this.errorIcon){
21219                     this.errorIcon.dom.qtip = '';
21220                     this.errorIcon.hide();
21221                     this.un('resize', this.alignErrorIcon, this);
21222                 }
21223                 break;
21224             default:
21225                 var t = Roo.getDom(this.msgTarget);
21226                 t.innerHTML = '';
21227                 t.style.display = 'none';
21228                 break;
21229         }
21230         this.fireEvent('valid', this);
21231     },
21232
21233     /**
21234      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21235      * @return {Mixed} value The field value
21236      */
21237     getRawValue : function(){
21238         var v = this.el.getValue();
21239         if(v === this.emptyText){
21240             v = '';
21241         }
21242         return v;
21243     },
21244
21245     /**
21246      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21247      * @return {Mixed} value The field value
21248      */
21249     getValue : function(){
21250         var v = this.el.getValue();
21251         if(v === this.emptyText || v === undefined){
21252             v = '';
21253         }
21254         return v;
21255     },
21256
21257     /**
21258      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21259      * @param {Mixed} value The value to set
21260      */
21261     setRawValue : function(v){
21262         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21263     },
21264
21265     /**
21266      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21267      * @param {Mixed} value The value to set
21268      */
21269     setValue : function(v){
21270         this.value = v;
21271         if(this.rendered){
21272             this.el.dom.value = (v === null || v === undefined ? '' : v);
21273             this.validate();
21274         }
21275     },
21276
21277     adjustSize : function(w, h){
21278         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21279         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21280         return s;
21281     },
21282
21283     adjustWidth : function(tag, w){
21284         tag = tag.toLowerCase();
21285         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21286             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21287                 if(tag == 'input'){
21288                     return w + 2;
21289                 }
21290                 if(tag = 'textarea'){
21291                     return w-2;
21292                 }
21293             }else if(Roo.isOpera){
21294                 if(tag == 'input'){
21295                     return w + 2;
21296                 }
21297                 if(tag = 'textarea'){
21298                     return w-2;
21299                 }
21300             }
21301         }
21302         return w;
21303     }
21304 });
21305
21306
21307 // anything other than normal should be considered experimental
21308 Roo.form.Field.msgFx = {
21309     normal : {
21310         show: function(msgEl, f){
21311             msgEl.setDisplayed('block');
21312         },
21313
21314         hide : function(msgEl, f){
21315             msgEl.setDisplayed(false).update('');
21316         }
21317     },
21318
21319     slide : {
21320         show: function(msgEl, f){
21321             msgEl.slideIn('t', {stopFx:true});
21322         },
21323
21324         hide : function(msgEl, f){
21325             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21326         }
21327     },
21328
21329     slideRight : {
21330         show: function(msgEl, f){
21331             msgEl.fixDisplay();
21332             msgEl.alignTo(f.el, 'tl-tr');
21333             msgEl.slideIn('l', {stopFx:true});
21334         },
21335
21336         hide : function(msgEl, f){
21337             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21338         }
21339     }
21340 };/*
21341  * Based on:
21342  * Ext JS Library 1.1.1
21343  * Copyright(c) 2006-2007, Ext JS, LLC.
21344  *
21345  * Originally Released Under LGPL - original licence link has changed is not relivant.
21346  *
21347  * Fork - LGPL
21348  * <script type="text/javascript">
21349  */
21350  
21351
21352 /**
21353  * @class Roo.form.TextField
21354  * @extends Roo.form.Field
21355  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21356  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21357  * @constructor
21358  * Creates a new TextField
21359  * @param {Object} config Configuration options
21360  */
21361 Roo.form.TextField = function(config){
21362     Roo.form.TextField.superclass.constructor.call(this, config);
21363     this.addEvents({
21364         /**
21365          * @event autosize
21366          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21367          * according to the default logic, but this event provides a hook for the developer to apply additional
21368          * logic at runtime to resize the field if needed.
21369              * @param {Roo.form.Field} this This text field
21370              * @param {Number} width The new field width
21371              */
21372         autosize : true
21373     });
21374 };
21375
21376 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21377     /**
21378      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21379      */
21380     grow : false,
21381     /**
21382      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21383      */
21384     growMin : 30,
21385     /**
21386      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21387      */
21388     growMax : 800,
21389     /**
21390      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21391      */
21392     vtype : null,
21393     /**
21394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21395      */
21396     maskRe : null,
21397     /**
21398      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21399      */
21400     disableKeyFilter : false,
21401     /**
21402      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21403      */
21404     allowBlank : true,
21405     /**
21406      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21407      */
21408     minLength : 0,
21409     /**
21410      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21411      */
21412     maxLength : Number.MAX_VALUE,
21413     /**
21414      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21415      */
21416     minLengthText : "The minimum length for this field is {0}",
21417     /**
21418      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21419      */
21420     maxLengthText : "The maximum length for this field is {0}",
21421     /**
21422      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21423      */
21424     selectOnFocus : false,
21425     /**
21426      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21427      */
21428     blankText : "This field is required",
21429     /**
21430      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21431      * If available, this function will be called only after the basic validators all return true, and will be passed the
21432      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21433      */
21434     validator : null,
21435     /**
21436      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21437      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21438      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21439      */
21440     regex : null,
21441     /**
21442      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21443      */
21444     regexText : "",
21445     /**
21446      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21447      */
21448     emptyText : null,
21449     /**
21450      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21451      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21452      */
21453     emptyClass : 'x-form-empty-field',
21454
21455     // private
21456     initEvents : function(){
21457         Roo.form.TextField.superclass.initEvents.call(this);
21458         if(this.validationEvent == 'keyup'){
21459             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21460             this.el.on('keyup', this.filterValidation, this);
21461         }
21462         else if(this.validationEvent !== false){
21463             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21464         }
21465         if(this.selectOnFocus || this.emptyText){
21466             this.on("focus", this.preFocus, this);
21467             if(this.emptyText){
21468                 this.on('blur', this.postBlur, this);
21469                 this.applyEmptyText();
21470             }
21471         }
21472         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21473             this.el.on("keypress", this.filterKeys, this);
21474         }
21475         if(this.grow){
21476             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21477             this.el.on("click", this.autoSize,  this);
21478         }
21479     },
21480
21481     processValue : function(value){
21482         if(this.stripCharsRe){
21483             var newValue = value.replace(this.stripCharsRe, '');
21484             if(newValue !== value){
21485                 this.setRawValue(newValue);
21486                 return newValue;
21487             }
21488         }
21489         return value;
21490     },
21491
21492     filterValidation : function(e){
21493         if(!e.isNavKeyPress()){
21494             this.validationTask.delay(this.validationDelay);
21495         }
21496     },
21497
21498     // private
21499     onKeyUp : function(e){
21500         if(!e.isNavKeyPress()){
21501             this.autoSize();
21502         }
21503     },
21504
21505     /**
21506      * Resets the current field value to the originally-loaded value and clears any validation messages.
21507      * Also adds emptyText and emptyClass if the original value was blank.
21508      */
21509     reset : function(){
21510         Roo.form.TextField.superclass.reset.call(this);
21511         this.applyEmptyText();
21512     },
21513
21514     applyEmptyText : function(){
21515         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21516             this.setRawValue(this.emptyText);
21517             this.el.addClass(this.emptyClass);
21518         }
21519     },
21520
21521     // private
21522     preFocus : function(){
21523         if(this.emptyText){
21524             if(this.el.dom.value == this.emptyText){
21525                 this.setRawValue('');
21526             }
21527             this.el.removeClass(this.emptyClass);
21528         }
21529         if(this.selectOnFocus){
21530             this.el.dom.select();
21531         }
21532     },
21533
21534     // private
21535     postBlur : function(){
21536         this.applyEmptyText();
21537     },
21538
21539     // private
21540     filterKeys : function(e){
21541         var k = e.getKey();
21542         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21543             return;
21544         }
21545         var c = e.getCharCode(), cc = String.fromCharCode(c);
21546         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21547             return;
21548         }
21549         if(!this.maskRe.test(cc)){
21550             e.stopEvent();
21551         }
21552     },
21553
21554     setValue : function(v){
21555         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21556             this.el.removeClass(this.emptyClass);
21557         }
21558         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21559         this.applyEmptyText();
21560         this.autoSize();
21561     },
21562
21563     /**
21564      * Validates a value according to the field's validation rules and marks the field as invalid
21565      * if the validation fails
21566      * @param {Mixed} value The value to validate
21567      * @return {Boolean} True if the value is valid, else false
21568      */
21569     validateValue : function(value){
21570         if(value.length < 1 || value === this.emptyText){ // if it's blank
21571              if(this.allowBlank){
21572                 this.clearInvalid();
21573                 return true;
21574              }else{
21575                 this.markInvalid(this.blankText);
21576                 return false;
21577              }
21578         }
21579         if(value.length < this.minLength){
21580             this.markInvalid(String.format(this.minLengthText, this.minLength));
21581             return false;
21582         }
21583         if(value.length > this.maxLength){
21584             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21585             return false;
21586         }
21587         if(this.vtype){
21588             var vt = Roo.form.VTypes;
21589             if(!vt[this.vtype](value, this)){
21590                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21591                 return false;
21592             }
21593         }
21594         if(typeof this.validator == "function"){
21595             var msg = this.validator(value);
21596             if(msg !== true){
21597                 this.markInvalid(msg);
21598                 return false;
21599             }
21600         }
21601         if(this.regex && !this.regex.test(value)){
21602             this.markInvalid(this.regexText);
21603             return false;
21604         }
21605         return true;
21606     },
21607
21608     /**
21609      * Selects text in this field
21610      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21611      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21612      */
21613     selectText : function(start, end){
21614         var v = this.getRawValue();
21615         if(v.length > 0){
21616             start = start === undefined ? 0 : start;
21617             end = end === undefined ? v.length : end;
21618             var d = this.el.dom;
21619             if(d.setSelectionRange){
21620                 d.setSelectionRange(start, end);
21621             }else if(d.createTextRange){
21622                 var range = d.createTextRange();
21623                 range.moveStart("character", start);
21624                 range.moveEnd("character", v.length-end);
21625                 range.select();
21626             }
21627         }
21628     },
21629
21630     /**
21631      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21632      * This only takes effect if grow = true, and fires the autosize event.
21633      */
21634     autoSize : function(){
21635         if(!this.grow || !this.rendered){
21636             return;
21637         }
21638         if(!this.metrics){
21639             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21640         }
21641         var el = this.el;
21642         var v = el.dom.value;
21643         var d = document.createElement('div');
21644         d.appendChild(document.createTextNode(v));
21645         v = d.innerHTML;
21646         d = null;
21647         v += "&#160;";
21648         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21649         this.el.setWidth(w);
21650         this.fireEvent("autosize", this, w);
21651     }
21652 });/*
21653  * Based on:
21654  * Ext JS Library 1.1.1
21655  * Copyright(c) 2006-2007, Ext JS, LLC.
21656  *
21657  * Originally Released Under LGPL - original licence link has changed is not relivant.
21658  *
21659  * Fork - LGPL
21660  * <script type="text/javascript">
21661  */
21662  
21663 /**
21664  * @class Roo.form.Hidden
21665  * @extends Roo.form.TextField
21666  * Simple Hidden element used on forms 
21667  * 
21668  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21669  * 
21670  * @constructor
21671  * Creates a new Hidden form element.
21672  * @param {Object} config Configuration options
21673  */
21674
21675
21676
21677 // easy hidden field...
21678 Roo.form.Hidden = function(config){
21679     Roo.form.Hidden.superclass.constructor.call(this, config);
21680 };
21681   
21682 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21683     fieldLabel:      '',
21684     inputType:      'hidden',
21685     width:          50,
21686     allowBlank:     true,
21687     labelSeparator: '',
21688     hidden:         true,
21689     itemCls :       'x-form-item-display-none'
21690
21691
21692 });
21693
21694
21695 /*
21696  * Based on:
21697  * Ext JS Library 1.1.1
21698  * Copyright(c) 2006-2007, Ext JS, LLC.
21699  *
21700  * Originally Released Under LGPL - original licence link has changed is not relivant.
21701  *
21702  * Fork - LGPL
21703  * <script type="text/javascript">
21704  */
21705  
21706 /**
21707  * @class Roo.form.TriggerField
21708  * @extends Roo.form.TextField
21709  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21710  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21711  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21712  * for which you can provide a custom implementation.  For example:
21713  * <pre><code>
21714 var trigger = new Roo.form.TriggerField();
21715 trigger.onTriggerClick = myTriggerFn;
21716 trigger.applyTo('my-field');
21717 </code></pre>
21718  *
21719  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21720  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21721  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21722  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21723  * @constructor
21724  * Create a new TriggerField.
21725  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21726  * to the base TextField)
21727  */
21728 Roo.form.TriggerField = function(config){
21729     this.mimicing = false;
21730     Roo.form.TriggerField.superclass.constructor.call(this, config);
21731 };
21732
21733 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21734     /**
21735      * @cfg {String} triggerClass A CSS class to apply to the trigger
21736      */
21737     /**
21738      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21739      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21740      */
21741     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21742     /**
21743      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21744      */
21745     hideTrigger:false,
21746
21747     /** @cfg {Boolean} grow @hide */
21748     /** @cfg {Number} growMin @hide */
21749     /** @cfg {Number} growMax @hide */
21750
21751     /**
21752      * @hide 
21753      * @method
21754      */
21755     autoSize: Roo.emptyFn,
21756     // private
21757     monitorTab : true,
21758     // private
21759     deferHeight : true,
21760
21761     
21762     actionMode : 'wrap',
21763     // private
21764     onResize : function(w, h){
21765         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21766         if(typeof w == 'number'){
21767             var x = w - this.trigger.getWidth();
21768             this.el.setWidth(this.adjustWidth('input', x));
21769             this.trigger.setStyle('left', x+'px');
21770         }
21771     },
21772
21773     // private
21774     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21775
21776     // private
21777     getResizeEl : function(){
21778         return this.wrap;
21779     },
21780
21781     // private
21782     getPositionEl : function(){
21783         return this.wrap;
21784     },
21785
21786     // private
21787     alignErrorIcon : function(){
21788         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21789     },
21790
21791     // private
21792     onRender : function(ct, position){
21793         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21794         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21795         this.trigger = this.wrap.createChild(this.triggerConfig ||
21796                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21797         if(this.hideTrigger){
21798             this.trigger.setDisplayed(false);
21799         }
21800         this.initTrigger();
21801         if(!this.width){
21802             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21803         }
21804     },
21805
21806     // private
21807     initTrigger : function(){
21808         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21809         this.trigger.addClassOnOver('x-form-trigger-over');
21810         this.trigger.addClassOnClick('x-form-trigger-click');
21811     },
21812
21813     // private
21814     onDestroy : function(){
21815         if(this.trigger){
21816             this.trigger.removeAllListeners();
21817             this.trigger.remove();
21818         }
21819         if(this.wrap){
21820             this.wrap.remove();
21821         }
21822         Roo.form.TriggerField.superclass.onDestroy.call(this);
21823     },
21824
21825     // private
21826     onFocus : function(){
21827         Roo.form.TriggerField.superclass.onFocus.call(this);
21828         if(!this.mimicing){
21829             this.wrap.addClass('x-trigger-wrap-focus');
21830             this.mimicing = true;
21831             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21832             if(this.monitorTab){
21833                 this.el.on("keydown", this.checkTab, this);
21834             }
21835         }
21836     },
21837
21838     // private
21839     checkTab : function(e){
21840         if(e.getKey() == e.TAB){
21841             this.triggerBlur();
21842         }
21843     },
21844
21845     // private
21846     onBlur : function(){
21847         // do nothing
21848     },
21849
21850     // private
21851     mimicBlur : function(e, t){
21852         if(!this.wrap.contains(t) && this.validateBlur()){
21853             this.triggerBlur();
21854         }
21855     },
21856
21857     // private
21858     triggerBlur : function(){
21859         this.mimicing = false;
21860         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21861         if(this.monitorTab){
21862             this.el.un("keydown", this.checkTab, this);
21863         }
21864         this.wrap.removeClass('x-trigger-wrap-focus');
21865         Roo.form.TriggerField.superclass.onBlur.call(this);
21866     },
21867
21868     // private
21869     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21870     validateBlur : function(e, t){
21871         return true;
21872     },
21873
21874     // private
21875     onDisable : function(){
21876         Roo.form.TriggerField.superclass.onDisable.call(this);
21877         if(this.wrap){
21878             this.wrap.addClass('x-item-disabled');
21879         }
21880     },
21881
21882     // private
21883     onEnable : function(){
21884         Roo.form.TriggerField.superclass.onEnable.call(this);
21885         if(this.wrap){
21886             this.wrap.removeClass('x-item-disabled');
21887         }
21888     },
21889
21890     // private
21891     onShow : function(){
21892         var ae = this.getActionEl();
21893         
21894         if(ae){
21895             ae.dom.style.display = '';
21896             ae.dom.style.visibility = 'visible';
21897         }
21898     },
21899
21900     // private
21901     
21902     onHide : function(){
21903         var ae = this.getActionEl();
21904         ae.dom.style.display = 'none';
21905     },
21906
21907     /**
21908      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21909      * by an implementing function.
21910      * @method
21911      * @param {EventObject} e
21912      */
21913     onTriggerClick : Roo.emptyFn
21914 });
21915
21916 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21917 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21918 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21919 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21920     initComponent : function(){
21921         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21922
21923         this.triggerConfig = {
21924             tag:'span', cls:'x-form-twin-triggers', cn:[
21925             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21926             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21927         ]};
21928     },
21929
21930     getTrigger : function(index){
21931         return this.triggers[index];
21932     },
21933
21934     initTrigger : function(){
21935         var ts = this.trigger.select('.x-form-trigger', true);
21936         this.wrap.setStyle('overflow', 'hidden');
21937         var triggerField = this;
21938         ts.each(function(t, all, index){
21939             t.hide = function(){
21940                 var w = triggerField.wrap.getWidth();
21941                 this.dom.style.display = 'none';
21942                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21943             };
21944             t.show = function(){
21945                 var w = triggerField.wrap.getWidth();
21946                 this.dom.style.display = '';
21947                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21948             };
21949             var triggerIndex = 'Trigger'+(index+1);
21950
21951             if(this['hide'+triggerIndex]){
21952                 t.dom.style.display = 'none';
21953             }
21954             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21955             t.addClassOnOver('x-form-trigger-over');
21956             t.addClassOnClick('x-form-trigger-click');
21957         }, this);
21958         this.triggers = ts.elements;
21959     },
21960
21961     onTrigger1Click : Roo.emptyFn,
21962     onTrigger2Click : Roo.emptyFn
21963 });/*
21964  * Based on:
21965  * Ext JS Library 1.1.1
21966  * Copyright(c) 2006-2007, Ext JS, LLC.
21967  *
21968  * Originally Released Under LGPL - original licence link has changed is not relivant.
21969  *
21970  * Fork - LGPL
21971  * <script type="text/javascript">
21972  */
21973  
21974 /**
21975  * @class Roo.form.TextArea
21976  * @extends Roo.form.TextField
21977  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21978  * support for auto-sizing.
21979  * @constructor
21980  * Creates a new TextArea
21981  * @param {Object} config Configuration options
21982  */
21983 Roo.form.TextArea = function(config){
21984     Roo.form.TextArea.superclass.constructor.call(this, config);
21985     // these are provided exchanges for backwards compat
21986     // minHeight/maxHeight were replaced by growMin/growMax to be
21987     // compatible with TextField growing config values
21988     if(this.minHeight !== undefined){
21989         this.growMin = this.minHeight;
21990     }
21991     if(this.maxHeight !== undefined){
21992         this.growMax = this.maxHeight;
21993     }
21994 };
21995
21996 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21997     /**
21998      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21999      */
22000     growMin : 60,
22001     /**
22002      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22003      */
22004     growMax: 1000,
22005     /**
22006      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22007      * in the field (equivalent to setting overflow: hidden, defaults to false)
22008      */
22009     preventScrollbars: false,
22010     /**
22011      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22012      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22013      */
22014
22015     // private
22016     onRender : function(ct, position){
22017         if(!this.el){
22018             this.defaultAutoCreate = {
22019                 tag: "textarea",
22020                 style:"width:300px;height:60px;",
22021                 autocomplete: "off"
22022             };
22023         }
22024         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22025         if(this.grow){
22026             this.textSizeEl = Roo.DomHelper.append(document.body, {
22027                 tag: "pre", cls: "x-form-grow-sizer"
22028             });
22029             if(this.preventScrollbars){
22030                 this.el.setStyle("overflow", "hidden");
22031             }
22032             this.el.setHeight(this.growMin);
22033         }
22034     },
22035
22036     onDestroy : function(){
22037         if(this.textSizeEl){
22038             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22039         }
22040         Roo.form.TextArea.superclass.onDestroy.call(this);
22041     },
22042
22043     // private
22044     onKeyUp : function(e){
22045         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22046             this.autoSize();
22047         }
22048     },
22049
22050     /**
22051      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22052      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22053      */
22054     autoSize : function(){
22055         if(!this.grow || !this.textSizeEl){
22056             return;
22057         }
22058         var el = this.el;
22059         var v = el.dom.value;
22060         var ts = this.textSizeEl;
22061
22062         ts.innerHTML = '';
22063         ts.appendChild(document.createTextNode(v));
22064         v = ts.innerHTML;
22065
22066         Roo.fly(ts).setWidth(this.el.getWidth());
22067         if(v.length < 1){
22068             v = "&#160;&#160;";
22069         }else{
22070             if(Roo.isIE){
22071                 v = v.replace(/\n/g, '<p>&#160;</p>');
22072             }
22073             v += "&#160;\n&#160;";
22074         }
22075         ts.innerHTML = v;
22076         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22077         if(h != this.lastHeight){
22078             this.lastHeight = h;
22079             this.el.setHeight(h);
22080             this.fireEvent("autosize", this, h);
22081         }
22082     }
22083 });/*
22084  * Based on:
22085  * Ext JS Library 1.1.1
22086  * Copyright(c) 2006-2007, Ext JS, LLC.
22087  *
22088  * Originally Released Under LGPL - original licence link has changed is not relivant.
22089  *
22090  * Fork - LGPL
22091  * <script type="text/javascript">
22092  */
22093  
22094
22095 /**
22096  * @class Roo.form.NumberField
22097  * @extends Roo.form.TextField
22098  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22099  * @constructor
22100  * Creates a new NumberField
22101  * @param {Object} config Configuration options
22102  */
22103 Roo.form.NumberField = function(config){
22104     Roo.form.NumberField.superclass.constructor.call(this, config);
22105 };
22106
22107 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22108     /**
22109      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22110      */
22111     fieldClass: "x-form-field x-form-num-field",
22112     /**
22113      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22114      */
22115     allowDecimals : true,
22116     /**
22117      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22118      */
22119     decimalSeparator : ".",
22120     /**
22121      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22122      */
22123     decimalPrecision : 2,
22124     /**
22125      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22126      */
22127     allowNegative : true,
22128     /**
22129      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22130      */
22131     minValue : Number.NEGATIVE_INFINITY,
22132     /**
22133      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22134      */
22135     maxValue : Number.MAX_VALUE,
22136     /**
22137      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22138      */
22139     minText : "The minimum value for this field is {0}",
22140     /**
22141      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22142      */
22143     maxText : "The maximum value for this field is {0}",
22144     /**
22145      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22146      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22147      */
22148     nanText : "{0} is not a valid number",
22149
22150     // private
22151     initEvents : function(){
22152         Roo.form.NumberField.superclass.initEvents.call(this);
22153         var allowed = "0123456789";
22154         if(this.allowDecimals){
22155             allowed += this.decimalSeparator;
22156         }
22157         if(this.allowNegative){
22158             allowed += "-";
22159         }
22160         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22161         var keyPress = function(e){
22162             var k = e.getKey();
22163             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22164                 return;
22165             }
22166             var c = e.getCharCode();
22167             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22168                 e.stopEvent();
22169             }
22170         };
22171         this.el.on("keypress", keyPress, this);
22172     },
22173
22174     // private
22175     validateValue : function(value){
22176         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22177             return false;
22178         }
22179         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22180              return true;
22181         }
22182         var num = this.parseValue(value);
22183         if(isNaN(num)){
22184             this.markInvalid(String.format(this.nanText, value));
22185             return false;
22186         }
22187         if(num < this.minValue){
22188             this.markInvalid(String.format(this.minText, this.minValue));
22189             return false;
22190         }
22191         if(num > this.maxValue){
22192             this.markInvalid(String.format(this.maxText, this.maxValue));
22193             return false;
22194         }
22195         return true;
22196     },
22197
22198     getValue : function(){
22199         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22200     },
22201
22202     // private
22203     parseValue : function(value){
22204         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22205         return isNaN(value) ? '' : value;
22206     },
22207
22208     // private
22209     fixPrecision : function(value){
22210         var nan = isNaN(value);
22211         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22212             return nan ? '' : value;
22213         }
22214         return parseFloat(value).toFixed(this.decimalPrecision);
22215     },
22216
22217     setValue : function(v){
22218         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22219     },
22220
22221     // private
22222     decimalPrecisionFcn : function(v){
22223         return Math.floor(v);
22224     },
22225
22226     beforeBlur : function(){
22227         var v = this.parseValue(this.getRawValue());
22228         if(v){
22229             this.setValue(this.fixPrecision(v));
22230         }
22231     }
22232 });/*
22233  * Based on:
22234  * Ext JS Library 1.1.1
22235  * Copyright(c) 2006-2007, Ext JS, LLC.
22236  *
22237  * Originally Released Under LGPL - original licence link has changed is not relivant.
22238  *
22239  * Fork - LGPL
22240  * <script type="text/javascript">
22241  */
22242  
22243 /**
22244  * @class Roo.form.DateField
22245  * @extends Roo.form.TriggerField
22246  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22247 * @constructor
22248 * Create a new DateField
22249 * @param {Object} config
22250  */
22251 Roo.form.DateField = function(config){
22252     Roo.form.DateField.superclass.constructor.call(this, config);
22253     
22254       this.addEvents({
22255          
22256         /**
22257          * @event select
22258          * Fires when a date is selected
22259              * @param {Roo.form.DateField} combo This combo box
22260              * @param {Date} date The date selected
22261              */
22262         'select' : true
22263          
22264     });
22265     
22266     
22267     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22268     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22269     this.ddMatch = null;
22270     if(this.disabledDates){
22271         var dd = this.disabledDates;
22272         var re = "(?:";
22273         for(var i = 0; i < dd.length; i++){
22274             re += dd[i];
22275             if(i != dd.length-1) re += "|";
22276         }
22277         this.ddMatch = new RegExp(re + ")");
22278     }
22279 };
22280
22281 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22282     /**
22283      * @cfg {String} format
22284      * The default date format string which can be overriden for localization support.  The format must be
22285      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22286      */
22287     format : "m/d/y",
22288     /**
22289      * @cfg {String} altFormats
22290      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22291      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22292      */
22293     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22294     /**
22295      * @cfg {Array} disabledDays
22296      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22297      */
22298     disabledDays : null,
22299     /**
22300      * @cfg {String} disabledDaysText
22301      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22302      */
22303     disabledDaysText : "Disabled",
22304     /**
22305      * @cfg {Array} disabledDates
22306      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22307      * expression so they are very powerful. Some examples:
22308      * <ul>
22309      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22310      * <li>["03/08", "09/16"] would disable those days for every year</li>
22311      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22312      * <li>["03/../2006"] would disable every day in March 2006</li>
22313      * <li>["^03"] would disable every day in every March</li>
22314      * </ul>
22315      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22316      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22317      */
22318     disabledDates : null,
22319     /**
22320      * @cfg {String} disabledDatesText
22321      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22322      */
22323     disabledDatesText : "Disabled",
22324     /**
22325      * @cfg {Date/String} minValue
22326      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22327      * valid format (defaults to null).
22328      */
22329     minValue : null,
22330     /**
22331      * @cfg {Date/String} maxValue
22332      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22333      * valid format (defaults to null).
22334      */
22335     maxValue : null,
22336     /**
22337      * @cfg {String} minText
22338      * The error text to display when the date in the cell is before minValue (defaults to
22339      * 'The date in this field must be after {minValue}').
22340      */
22341     minText : "The date in this field must be equal to or after {0}",
22342     /**
22343      * @cfg {String} maxText
22344      * The error text to display when the date in the cell is after maxValue (defaults to
22345      * 'The date in this field must be before {maxValue}').
22346      */
22347     maxText : "The date in this field must be equal to or before {0}",
22348     /**
22349      * @cfg {String} invalidText
22350      * The error text to display when the date in the field is invalid (defaults to
22351      * '{value} is not a valid date - it must be in the format {format}').
22352      */
22353     invalidText : "{0} is not a valid date - it must be in the format {1}",
22354     /**
22355      * @cfg {String} triggerClass
22356      * An additional CSS class used to style the trigger button.  The trigger will always get the
22357      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22358      * which displays a calendar icon).
22359      */
22360     triggerClass : 'x-form-date-trigger',
22361     
22362
22363     /**
22364      * @cfg {bool} useIso
22365      * if enabled, then the date field will use a hidden field to store the 
22366      * real value as iso formated date. default (false)
22367      */ 
22368     useIso : false,
22369     /**
22370      * @cfg {String/Object} autoCreate
22371      * A DomHelper element spec, or true for a default element spec (defaults to
22372      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22373      */ 
22374     // private
22375     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22376     
22377     // private
22378     hiddenField: false,
22379     
22380     onRender : function(ct, position)
22381     {
22382         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22383         if (this.useIso) {
22384             this.el.dom.removeAttribute('name'); 
22385             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22386                     'before', true);
22387             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22388             // prevent input submission
22389             this.hiddenName = this.name;
22390         }
22391             
22392             
22393     },
22394     
22395     // private
22396     validateValue : function(value)
22397     {
22398         value = this.formatDate(value);
22399         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22400             return false;
22401         }
22402         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22403              return true;
22404         }
22405         var svalue = value;
22406         value = this.parseDate(value);
22407         if(!value){
22408             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22409             return false;
22410         }
22411         var time = value.getTime();
22412         if(this.minValue && time < this.minValue.getTime()){
22413             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22414             return false;
22415         }
22416         if(this.maxValue && time > this.maxValue.getTime()){
22417             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22418             return false;
22419         }
22420         if(this.disabledDays){
22421             var day = value.getDay();
22422             for(var i = 0; i < this.disabledDays.length; i++) {
22423                 if(day === this.disabledDays[i]){
22424                     this.markInvalid(this.disabledDaysText);
22425                     return false;
22426                 }
22427             }
22428         }
22429         var fvalue = this.formatDate(value);
22430         if(this.ddMatch && this.ddMatch.test(fvalue)){
22431             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22432             return false;
22433         }
22434         return true;
22435     },
22436
22437     // private
22438     // Provides logic to override the default TriggerField.validateBlur which just returns true
22439     validateBlur : function(){
22440         return !this.menu || !this.menu.isVisible();
22441     },
22442
22443     /**
22444      * Returns the current date value of the date field.
22445      * @return {Date} The date value
22446      */
22447     getValue : function(){
22448         
22449         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22450     },
22451
22452     /**
22453      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22454      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22455      * (the default format used is "m/d/y").
22456      * <br />Usage:
22457      * <pre><code>
22458 //All of these calls set the same date value (May 4, 2006)
22459
22460 //Pass a date object:
22461 var dt = new Date('5/4/06');
22462 dateField.setValue(dt);
22463
22464 //Pass a date string (default format):
22465 dateField.setValue('5/4/06');
22466
22467 //Pass a date string (custom format):
22468 dateField.format = 'Y-m-d';
22469 dateField.setValue('2006-5-4');
22470 </code></pre>
22471      * @param {String/Date} date The date or valid date string
22472      */
22473     setValue : function(date){
22474         if (this.hiddenField) {
22475             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22476         }
22477         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22478     },
22479
22480     // private
22481     parseDate : function(value){
22482         if(!value || value instanceof Date){
22483             return value;
22484         }
22485         var v = Date.parseDate(value, this.format);
22486         if(!v && this.altFormats){
22487             if(!this.altFormatsArray){
22488                 this.altFormatsArray = this.altFormats.split("|");
22489             }
22490             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22491                 v = Date.parseDate(value, this.altFormatsArray[i]);
22492             }
22493         }
22494         return v;
22495     },
22496
22497     // private
22498     formatDate : function(date, fmt){
22499         return (!date || !(date instanceof Date)) ?
22500                date : date.dateFormat(fmt || this.format);
22501     },
22502
22503     // private
22504     menuListeners : {
22505         select: function(m, d){
22506             this.setValue(d);
22507             this.fireEvent('select', this, d);
22508         },
22509         show : function(){ // retain focus styling
22510             this.onFocus();
22511         },
22512         hide : function(){
22513             this.focus.defer(10, this);
22514             var ml = this.menuListeners;
22515             this.menu.un("select", ml.select,  this);
22516             this.menu.un("show", ml.show,  this);
22517             this.menu.un("hide", ml.hide,  this);
22518         }
22519     },
22520
22521     // private
22522     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22523     onTriggerClick : function(){
22524         if(this.disabled){
22525             return;
22526         }
22527         if(this.menu == null){
22528             this.menu = new Roo.menu.DateMenu();
22529         }
22530         Roo.apply(this.menu.picker,  {
22531             showClear: this.allowBlank,
22532             minDate : this.minValue,
22533             maxDate : this.maxValue,
22534             disabledDatesRE : this.ddMatch,
22535             disabledDatesText : this.disabledDatesText,
22536             disabledDays : this.disabledDays,
22537             disabledDaysText : this.disabledDaysText,
22538             format : this.format,
22539             minText : String.format(this.minText, this.formatDate(this.minValue)),
22540             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22541         });
22542         this.menu.on(Roo.apply({}, this.menuListeners, {
22543             scope:this
22544         }));
22545         this.menu.picker.setValue(this.getValue() || new Date());
22546         this.menu.show(this.el, "tl-bl?");
22547     },
22548
22549     beforeBlur : function(){
22550         var v = this.parseDate(this.getRawValue());
22551         if(v){
22552             this.setValue(v);
22553         }
22554     }
22555
22556     /** @cfg {Boolean} grow @hide */
22557     /** @cfg {Number} growMin @hide */
22558     /** @cfg {Number} growMax @hide */
22559     /**
22560      * @hide
22561      * @method autoSize
22562      */
22563 });/*
22564  * Based on:
22565  * Ext JS Library 1.1.1
22566  * Copyright(c) 2006-2007, Ext JS, LLC.
22567  *
22568  * Originally Released Under LGPL - original licence link has changed is not relivant.
22569  *
22570  * Fork - LGPL
22571  * <script type="text/javascript">
22572  */
22573  
22574
22575 /**
22576  * @class Roo.form.ComboBox
22577  * @extends Roo.form.TriggerField
22578  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22579  * @constructor
22580  * Create a new ComboBox.
22581  * @param {Object} config Configuration options
22582  */
22583 Roo.form.ComboBox = function(config){
22584     Roo.form.ComboBox.superclass.constructor.call(this, config);
22585     this.addEvents({
22586         /**
22587          * @event expand
22588          * Fires when the dropdown list is expanded
22589              * @param {Roo.form.ComboBox} combo This combo box
22590              */
22591         'expand' : true,
22592         /**
22593          * @event collapse
22594          * Fires when the dropdown list is collapsed
22595              * @param {Roo.form.ComboBox} combo This combo box
22596              */
22597         'collapse' : true,
22598         /**
22599          * @event beforeselect
22600          * Fires before a list item is selected. Return false to cancel the selection.
22601              * @param {Roo.form.ComboBox} combo This combo box
22602              * @param {Roo.data.Record} record The data record returned from the underlying store
22603              * @param {Number} index The index of the selected item in the dropdown list
22604              */
22605         'beforeselect' : true,
22606         /**
22607          * @event select
22608          * Fires when a list item is selected
22609              * @param {Roo.form.ComboBox} combo This combo box
22610              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22611              * @param {Number} index The index of the selected item in the dropdown list
22612              */
22613         'select' : true,
22614         /**
22615          * @event beforequery
22616          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22617          * The event object passed has these properties:
22618              * @param {Roo.form.ComboBox} combo This combo box
22619              * @param {String} query The query
22620              * @param {Boolean} forceAll true to force "all" query
22621              * @param {Boolean} cancel true to cancel the query
22622              * @param {Object} e The query event object
22623              */
22624         'beforequery': true,
22625          /**
22626          * @event add
22627          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22628              * @param {Roo.form.ComboBox} combo This combo box
22629              */
22630         'add' : true,
22631         /**
22632          * @event edit
22633          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22634              * @param {Roo.form.ComboBox} combo This combo box
22635              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22636              */
22637         'edit' : true
22638         
22639         
22640     });
22641     if(this.transform){
22642         this.allowDomMove = false;
22643         var s = Roo.getDom(this.transform);
22644         if(!this.hiddenName){
22645             this.hiddenName = s.name;
22646         }
22647         if(!this.store){
22648             this.mode = 'local';
22649             var d = [], opts = s.options;
22650             for(var i = 0, len = opts.length;i < len; i++){
22651                 var o = opts[i];
22652                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22653                 if(o.selected) {
22654                     this.value = value;
22655                 }
22656                 d.push([value, o.text]);
22657             }
22658             this.store = new Roo.data.SimpleStore({
22659                 'id': 0,
22660                 fields: ['value', 'text'],
22661                 data : d
22662             });
22663             this.valueField = 'value';
22664             this.displayField = 'text';
22665         }
22666         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22667         if(!this.lazyRender){
22668             this.target = true;
22669             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22670             s.parentNode.removeChild(s); // remove it
22671             this.render(this.el.parentNode);
22672         }else{
22673             s.parentNode.removeChild(s); // remove it
22674         }
22675
22676     }
22677     if (this.store) {
22678         this.store = Roo.factory(this.store, Roo.data);
22679     }
22680     
22681     this.selectedIndex = -1;
22682     if(this.mode == 'local'){
22683         if(config.queryDelay === undefined){
22684             this.queryDelay = 10;
22685         }
22686         if(config.minChars === undefined){
22687             this.minChars = 0;
22688         }
22689     }
22690 };
22691
22692 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22693     /**
22694      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22695      */
22696     /**
22697      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22698      * rendering into an Roo.Editor, defaults to false)
22699      */
22700     /**
22701      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22702      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22703      */
22704     /**
22705      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22706      */
22707     /**
22708      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22709      * the dropdown list (defaults to undefined, with no header element)
22710      */
22711
22712      /**
22713      * @cfg {String/Roo.Template} tpl The template to use to render the output
22714      */
22715      
22716     // private
22717     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22718     /**
22719      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22720      */
22721     listWidth: undefined,
22722     /**
22723      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22724      * mode = 'remote' or 'text' if mode = 'local')
22725      */
22726     displayField: undefined,
22727     /**
22728      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22729      * mode = 'remote' or 'value' if mode = 'local'). 
22730      * Note: use of a valueField requires the user make a selection
22731      * in order for a value to be mapped.
22732      */
22733     valueField: undefined,
22734     /**
22735      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22736      * field's data value (defaults to the underlying DOM element's name)
22737      */
22738     hiddenName: undefined,
22739     /**
22740      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22741      */
22742     listClass: '',
22743     /**
22744      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22745      */
22746     selectedClass: 'x-combo-selected',
22747     /**
22748      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22749      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22750      * which displays a downward arrow icon).
22751      */
22752     triggerClass : 'x-form-arrow-trigger',
22753     /**
22754      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22755      */
22756     shadow:'sides',
22757     /**
22758      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22759      * anchor positions (defaults to 'tl-bl')
22760      */
22761     listAlign: 'tl-bl?',
22762     /**
22763      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22764      */
22765     maxHeight: 300,
22766     /**
22767      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22768      * query specified by the allQuery config option (defaults to 'query')
22769      */
22770     triggerAction: 'query',
22771     /**
22772      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22773      * (defaults to 4, does not apply if editable = false)
22774      */
22775     minChars : 4,
22776     /**
22777      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22778      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22779      */
22780     typeAhead: false,
22781     /**
22782      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22783      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22784      */
22785     queryDelay: 500,
22786     /**
22787      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22788      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22789      */
22790     pageSize: 0,
22791     /**
22792      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22793      * when editable = true (defaults to false)
22794      */
22795     selectOnFocus:false,
22796     /**
22797      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22798      */
22799     queryParam: 'query',
22800     /**
22801      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22802      * when mode = 'remote' (defaults to 'Loading...')
22803      */
22804     loadingText: 'Loading...',
22805     /**
22806      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22807      */
22808     resizable: false,
22809     /**
22810      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22811      */
22812     handleHeight : 8,
22813     /**
22814      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22815      * traditional select (defaults to true)
22816      */
22817     editable: true,
22818     /**
22819      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22820      */
22821     allQuery: '',
22822     /**
22823      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22824      */
22825     mode: 'remote',
22826     /**
22827      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22828      * listWidth has a higher value)
22829      */
22830     minListWidth : 70,
22831     /**
22832      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22833      * allow the user to set arbitrary text into the field (defaults to false)
22834      */
22835     forceSelection:false,
22836     /**
22837      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22838      * if typeAhead = true (defaults to 250)
22839      */
22840     typeAheadDelay : 250,
22841     /**
22842      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22843      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22844      */
22845     valueNotFoundText : undefined,
22846     /**
22847      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22848      */
22849     blockFocus : false,
22850     
22851     /**
22852      * @cfg {Boolean} disableClear Disable showing of clear button.
22853      */
22854     disableClear : false,
22855     /**
22856      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22857      */
22858     alwaysQuery : false,
22859     
22860     //private
22861     addicon : false,
22862     editicon: false,
22863     
22864     
22865     // private
22866     onRender : function(ct, position){
22867         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22868         if(this.hiddenName){
22869             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22870                     'before', true);
22871             this.hiddenField.value =
22872                 this.hiddenValue !== undefined ? this.hiddenValue :
22873                 this.value !== undefined ? this.value : '';
22874
22875             // prevent input submission
22876             this.el.dom.removeAttribute('name');
22877         }
22878         if(Roo.isGecko){
22879             this.el.dom.setAttribute('autocomplete', 'off');
22880         }
22881
22882         var cls = 'x-combo-list';
22883
22884         this.list = new Roo.Layer({
22885             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22886         });
22887
22888         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22889         this.list.setWidth(lw);
22890         this.list.swallowEvent('mousewheel');
22891         this.assetHeight = 0;
22892
22893         if(this.title){
22894             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22895             this.assetHeight += this.header.getHeight();
22896         }
22897
22898         this.innerList = this.list.createChild({cls:cls+'-inner'});
22899         this.innerList.on('mouseover', this.onViewOver, this);
22900         this.innerList.on('mousemove', this.onViewMove, this);
22901         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22902         
22903         if(this.allowBlank && !this.pageSize && !this.disableClear){
22904             this.footer = this.list.createChild({cls:cls+'-ft'});
22905             this.pageTb = new Roo.Toolbar(this.footer);
22906            
22907         }
22908         if(this.pageSize){
22909             this.footer = this.list.createChild({cls:cls+'-ft'});
22910             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22911                     {pageSize: this.pageSize});
22912             
22913         }
22914         
22915         if (this.pageTb && this.allowBlank && !this.disableClear) {
22916             var _this = this;
22917             this.pageTb.add(new Roo.Toolbar.Fill(), {
22918                 cls: 'x-btn-icon x-btn-clear',
22919                 text: '&#160;',
22920                 handler: function()
22921                 {
22922                     _this.collapse();
22923                     _this.clearValue();
22924                     _this.onSelect(false, -1);
22925                 }
22926             });
22927         }
22928         if (this.footer) {
22929             this.assetHeight += this.footer.getHeight();
22930         }
22931         
22932
22933         if(!this.tpl){
22934             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22935         }
22936
22937         this.view = new Roo.View(this.innerList, this.tpl, {
22938             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22939         });
22940
22941         this.view.on('click', this.onViewClick, this);
22942
22943         this.store.on('beforeload', this.onBeforeLoad, this);
22944         this.store.on('load', this.onLoad, this);
22945         this.store.on('loadexception', this.collapse, this);
22946
22947         if(this.resizable){
22948             this.resizer = new Roo.Resizable(this.list,  {
22949                pinned:true, handles:'se'
22950             });
22951             this.resizer.on('resize', function(r, w, h){
22952                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22953                 this.listWidth = w;
22954                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22955                 this.restrictHeight();
22956             }, this);
22957             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22958         }
22959         if(!this.editable){
22960             this.editable = true;
22961             this.setEditable(false);
22962         }  
22963         
22964         
22965         if (typeof(this.events.add.listeners) != 'undefined') {
22966             
22967             this.addicon = this.wrap.createChild(
22968                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22969        
22970             this.addicon.on('click', function(e) {
22971                 this.fireEvent('add', this);
22972             }, this);
22973         }
22974         if (typeof(this.events.edit.listeners) != 'undefined') {
22975             
22976             this.editicon = this.wrap.createChild(
22977                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22978             if (this.addicon) {
22979                 this.editicon.setStyle('margin-left', '40px');
22980             }
22981             this.editicon.on('click', function(e) {
22982                 
22983                 // we fire even  if inothing is selected..
22984                 this.fireEvent('edit', this, this.lastData );
22985                 
22986             }, this);
22987         }
22988         
22989         
22990         
22991     },
22992
22993     // private
22994     initEvents : function(){
22995         Roo.form.ComboBox.superclass.initEvents.call(this);
22996
22997         this.keyNav = new Roo.KeyNav(this.el, {
22998             "up" : function(e){
22999                 this.inKeyMode = true;
23000                 this.selectPrev();
23001             },
23002
23003             "down" : function(e){
23004                 if(!this.isExpanded()){
23005                     this.onTriggerClick();
23006                 }else{
23007                     this.inKeyMode = true;
23008                     this.selectNext();
23009                 }
23010             },
23011
23012             "enter" : function(e){
23013                 this.onViewClick();
23014                 //return true;
23015             },
23016
23017             "esc" : function(e){
23018                 this.collapse();
23019             },
23020
23021             "tab" : function(e){
23022                 this.onViewClick(false);
23023                 return true;
23024             },
23025
23026             scope : this,
23027
23028             doRelay : function(foo, bar, hname){
23029                 if(hname == 'down' || this.scope.isExpanded()){
23030                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23031                 }
23032                 return true;
23033             },
23034
23035             forceKeyDown: true
23036         });
23037         this.queryDelay = Math.max(this.queryDelay || 10,
23038                 this.mode == 'local' ? 10 : 250);
23039         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23040         if(this.typeAhead){
23041             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23042         }
23043         if(this.editable !== false){
23044             this.el.on("keyup", this.onKeyUp, this);
23045         }
23046         if(this.forceSelection){
23047             this.on('blur', this.doForce, this);
23048         }
23049     },
23050
23051     onDestroy : function(){
23052         if(this.view){
23053             this.view.setStore(null);
23054             this.view.el.removeAllListeners();
23055             this.view.el.remove();
23056             this.view.purgeListeners();
23057         }
23058         if(this.list){
23059             this.list.destroy();
23060         }
23061         if(this.store){
23062             this.store.un('beforeload', this.onBeforeLoad, this);
23063             this.store.un('load', this.onLoad, this);
23064             this.store.un('loadexception', this.collapse, this);
23065         }
23066         Roo.form.ComboBox.superclass.onDestroy.call(this);
23067     },
23068
23069     // private
23070     fireKey : function(e){
23071         if(e.isNavKeyPress() && !this.list.isVisible()){
23072             this.fireEvent("specialkey", this, e);
23073         }
23074     },
23075
23076     // private
23077     onResize: function(w, h){
23078         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23079         
23080         if(typeof w != 'number'){
23081             // we do not handle it!?!?
23082             return;
23083         }
23084         var tw = this.trigger.getWidth();
23085         tw += this.addicon ? this.addicon.getWidth() : 0;
23086         tw += this.editicon ? this.editicon.getWidth() : 0;
23087         var x = w - tw;
23088         this.el.setWidth( this.adjustWidth('input', x));
23089             
23090         this.trigger.setStyle('left', x+'px');
23091         
23092         if(this.list && this.listWidth === undefined){
23093             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23094             this.list.setWidth(lw);
23095             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23096         }
23097         
23098     
23099         
23100     },
23101
23102     /**
23103      * Allow or prevent the user from directly editing the field text.  If false is passed,
23104      * the user will only be able to select from the items defined in the dropdown list.  This method
23105      * is the runtime equivalent of setting the 'editable' config option at config time.
23106      * @param {Boolean} value True to allow the user to directly edit the field text
23107      */
23108     setEditable : function(value){
23109         if(value == this.editable){
23110             return;
23111         }
23112         this.editable = value;
23113         if(!value){
23114             this.el.dom.setAttribute('readOnly', true);
23115             this.el.on('mousedown', this.onTriggerClick,  this);
23116             this.el.addClass('x-combo-noedit');
23117         }else{
23118             this.el.dom.setAttribute('readOnly', false);
23119             this.el.un('mousedown', this.onTriggerClick,  this);
23120             this.el.removeClass('x-combo-noedit');
23121         }
23122     },
23123
23124     // private
23125     onBeforeLoad : function(){
23126         if(!this.hasFocus){
23127             return;
23128         }
23129         this.innerList.update(this.loadingText ?
23130                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23131         this.restrictHeight();
23132         this.selectedIndex = -1;
23133     },
23134
23135     // private
23136     onLoad : function(){
23137         if(!this.hasFocus){
23138             return;
23139         }
23140         if(this.store.getCount() > 0){
23141             this.expand();
23142             this.restrictHeight();
23143             if(this.lastQuery == this.allQuery){
23144                 if(this.editable){
23145                     this.el.dom.select();
23146                 }
23147                 if(!this.selectByValue(this.value, true)){
23148                     this.select(0, true);
23149                 }
23150             }else{
23151                 this.selectNext();
23152                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23153                     this.taTask.delay(this.typeAheadDelay);
23154                 }
23155             }
23156         }else{
23157             this.onEmptyResults();
23158         }
23159         //this.el.focus();
23160     },
23161
23162     // private
23163     onTypeAhead : function(){
23164         if(this.store.getCount() > 0){
23165             var r = this.store.getAt(0);
23166             var newValue = r.data[this.displayField];
23167             var len = newValue.length;
23168             var selStart = this.getRawValue().length;
23169             if(selStart != len){
23170                 this.setRawValue(newValue);
23171                 this.selectText(selStart, newValue.length);
23172             }
23173         }
23174     },
23175
23176     // private
23177     onSelect : function(record, index){
23178         if(this.fireEvent('beforeselect', this, record, index) !== false){
23179             this.setFromData(index > -1 ? record.data : false);
23180             this.collapse();
23181             this.fireEvent('select', this, record, index);
23182         }
23183     },
23184
23185     /**
23186      * Returns the currently selected field value or empty string if no value is set.
23187      * @return {String} value The selected value
23188      */
23189     getValue : function(){
23190         if(this.valueField){
23191             return typeof this.value != 'undefined' ? this.value : '';
23192         }else{
23193             return Roo.form.ComboBox.superclass.getValue.call(this);
23194         }
23195     },
23196
23197     /**
23198      * Clears any text/value currently set in the field
23199      */
23200     clearValue : function(){
23201         if(this.hiddenField){
23202             this.hiddenField.value = '';
23203         }
23204         this.value = '';
23205         this.setRawValue('');
23206         this.lastSelectionText = '';
23207         this.applyEmptyText();
23208     },
23209
23210     /**
23211      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23212      * will be displayed in the field.  If the value does not match the data value of an existing item,
23213      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23214      * Otherwise the field will be blank (although the value will still be set).
23215      * @param {String} value The value to match
23216      */
23217     setValue : function(v){
23218         var text = v;
23219         if(this.valueField){
23220             var r = this.findRecord(this.valueField, v);
23221             if(r){
23222                 text = r.data[this.displayField];
23223             }else if(this.valueNotFoundText !== undefined){
23224                 text = this.valueNotFoundText;
23225             }
23226         }
23227         this.lastSelectionText = text;
23228         if(this.hiddenField){
23229             this.hiddenField.value = v;
23230         }
23231         Roo.form.ComboBox.superclass.setValue.call(this, text);
23232         this.value = v;
23233     },
23234     /**
23235      * @property {Object} the last set data for the element
23236      */
23237     
23238     lastData : false,
23239     /**
23240      * Sets the value of the field based on a object which is related to the record format for the store.
23241      * @param {Object} value the value to set as. or false on reset?
23242      */
23243     setFromData : function(o){
23244         var dv = ''; // display value
23245         var vv = ''; // value value..
23246         this.lastData = o;
23247         if (this.displayField) {
23248             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23249         } else {
23250             // this is an error condition!!!
23251             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23252         }
23253         
23254         if(this.valueField){
23255             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23256         }
23257         if(this.hiddenField){
23258             this.hiddenField.value = vv;
23259             
23260             this.lastSelectionText = dv;
23261             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23262             this.value = vv;
23263             return;
23264         }
23265         // no hidden field.. - we store the value in 'value', but still display
23266         // display field!!!!
23267         this.lastSelectionText = dv;
23268         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23269         this.value = vv;
23270         
23271         
23272     },
23273     // private
23274     reset : function(){
23275         // overridden so that last data is reset..
23276         this.setValue(this.originalValue);
23277         this.clearInvalid();
23278         this.lastData = false;
23279     },
23280     // private
23281     findRecord : function(prop, value){
23282         var record;
23283         if(this.store.getCount() > 0){
23284             this.store.each(function(r){
23285                 if(r.data[prop] == value){
23286                     record = r;
23287                     return false;
23288                 }
23289             });
23290         }
23291         return record;
23292     },
23293
23294     // private
23295     onViewMove : function(e, t){
23296         this.inKeyMode = false;
23297     },
23298
23299     // private
23300     onViewOver : function(e, t){
23301         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23302             return;
23303         }
23304         var item = this.view.findItemFromChild(t);
23305         if(item){
23306             var index = this.view.indexOf(item);
23307             this.select(index, false);
23308         }
23309     },
23310
23311     // private
23312     onViewClick : function(doFocus){
23313         var index = this.view.getSelectedIndexes()[0];
23314         var r = this.store.getAt(index);
23315         if(r){
23316             this.onSelect(r, index);
23317         }
23318         if(doFocus !== false && !this.blockFocus){
23319             this.el.focus();
23320         }
23321     },
23322
23323     // private
23324     restrictHeight : function(){
23325         this.innerList.dom.style.height = '';
23326         var inner = this.innerList.dom;
23327         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23328         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23329         this.list.beginUpdate();
23330         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23331         this.list.alignTo(this.el, this.listAlign);
23332         this.list.endUpdate();
23333     },
23334
23335     // private
23336     onEmptyResults : function(){
23337         this.collapse();
23338     },
23339
23340     /**
23341      * Returns true if the dropdown list is expanded, else false.
23342      */
23343     isExpanded : function(){
23344         return this.list.isVisible();
23345     },
23346
23347     /**
23348      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23349      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23350      * @param {String} value The data value of the item to select
23351      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23352      * selected item if it is not currently in view (defaults to true)
23353      * @return {Boolean} True if the value matched an item in the list, else false
23354      */
23355     selectByValue : function(v, scrollIntoView){
23356         if(v !== undefined && v !== null){
23357             var r = this.findRecord(this.valueField || this.displayField, v);
23358             if(r){
23359                 this.select(this.store.indexOf(r), scrollIntoView);
23360                 return true;
23361             }
23362         }
23363         return false;
23364     },
23365
23366     /**
23367      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23368      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23369      * @param {Number} index The zero-based index of the list item to select
23370      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23371      * selected item if it is not currently in view (defaults to true)
23372      */
23373     select : function(index, scrollIntoView){
23374         this.selectedIndex = index;
23375         this.view.select(index);
23376         if(scrollIntoView !== false){
23377             var el = this.view.getNode(index);
23378             if(el){
23379                 this.innerList.scrollChildIntoView(el, false);
23380             }
23381         }
23382     },
23383
23384     // private
23385     selectNext : function(){
23386         var ct = this.store.getCount();
23387         if(ct > 0){
23388             if(this.selectedIndex == -1){
23389                 this.select(0);
23390             }else if(this.selectedIndex < ct-1){
23391                 this.select(this.selectedIndex+1);
23392             }
23393         }
23394     },
23395
23396     // private
23397     selectPrev : function(){
23398         var ct = this.store.getCount();
23399         if(ct > 0){
23400             if(this.selectedIndex == -1){
23401                 this.select(0);
23402             }else if(this.selectedIndex != 0){
23403                 this.select(this.selectedIndex-1);
23404             }
23405         }
23406     },
23407
23408     // private
23409     onKeyUp : function(e){
23410         if(this.editable !== false && !e.isSpecialKey()){
23411             this.lastKey = e.getKey();
23412             this.dqTask.delay(this.queryDelay);
23413         }
23414     },
23415
23416     // private
23417     validateBlur : function(){
23418         return !this.list || !this.list.isVisible();   
23419     },
23420
23421     // private
23422     initQuery : function(){
23423         this.doQuery(this.getRawValue());
23424     },
23425
23426     // private
23427     doForce : function(){
23428         if(this.el.dom.value.length > 0){
23429             this.el.dom.value =
23430                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23431             this.applyEmptyText();
23432         }
23433     },
23434
23435     /**
23436      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23437      * query allowing the query action to be canceled if needed.
23438      * @param {String} query The SQL query to execute
23439      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23440      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23441      * saved in the current store (defaults to false)
23442      */
23443     doQuery : function(q, forceAll){
23444         if(q === undefined || q === null){
23445             q = '';
23446         }
23447         var qe = {
23448             query: q,
23449             forceAll: forceAll,
23450             combo: this,
23451             cancel:false
23452         };
23453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23454             return false;
23455         }
23456         q = qe.query;
23457         forceAll = qe.forceAll;
23458         if(forceAll === true || (q.length >= this.minChars)){
23459             if(this.lastQuery != q || this.alwaysQuery){
23460                 this.lastQuery = q;
23461                 if(this.mode == 'local'){
23462                     this.selectedIndex = -1;
23463                     if(forceAll){
23464                         this.store.clearFilter();
23465                     }else{
23466                         this.store.filter(this.displayField, q);
23467                     }
23468                     this.onLoad();
23469                 }else{
23470                     this.store.baseParams[this.queryParam] = q;
23471                     this.store.load({
23472                         params: this.getParams(q)
23473                     });
23474                     this.expand();
23475                 }
23476             }else{
23477                 this.selectedIndex = -1;
23478                 this.onLoad();   
23479             }
23480         }
23481     },
23482
23483     // private
23484     getParams : function(q){
23485         var p = {};
23486         //p[this.queryParam] = q;
23487         if(this.pageSize){
23488             p.start = 0;
23489             p.limit = this.pageSize;
23490         }
23491         return p;
23492     },
23493
23494     /**
23495      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23496      */
23497     collapse : function(){
23498         if(!this.isExpanded()){
23499             return;
23500         }
23501         this.list.hide();
23502         Roo.get(document).un('mousedown', this.collapseIf, this);
23503         Roo.get(document).un('mousewheel', this.collapseIf, this);
23504         if (!this.editable) {
23505             Roo.get(document).un('keydown', this.listKeyPress, this);
23506         }
23507         this.fireEvent('collapse', this);
23508     },
23509
23510     // private
23511     collapseIf : function(e){
23512         if(!e.within(this.wrap) && !e.within(this.list)){
23513             this.collapse();
23514         }
23515     },
23516
23517     /**
23518      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23519      */
23520     expand : function(){
23521         if(this.isExpanded() || !this.hasFocus){
23522             return;
23523         }
23524         this.list.alignTo(this.el, this.listAlign);
23525         this.list.show();
23526         Roo.get(document).on('mousedown', this.collapseIf, this);
23527         Roo.get(document).on('mousewheel', this.collapseIf, this);
23528         if (!this.editable) {
23529             Roo.get(document).on('keydown', this.listKeyPress, this);
23530         }
23531         
23532         this.fireEvent('expand', this);
23533     },
23534
23535     // private
23536     // Implements the default empty TriggerField.onTriggerClick function
23537     onTriggerClick : function(){
23538         if(this.disabled){
23539             return;
23540         }
23541         if(this.isExpanded()){
23542             this.collapse();
23543             if (!this.blockFocus) {
23544                 this.el.focus();
23545             }
23546             
23547         }else {
23548             this.hasFocus = true;
23549             if(this.triggerAction == 'all') {
23550                 this.doQuery(this.allQuery, true);
23551             } else {
23552                 this.doQuery(this.getRawValue());
23553             }
23554             if (!this.blockFocus) {
23555                 this.el.focus();
23556             }
23557         }
23558     },
23559     listKeyPress : function(e)
23560     {
23561         //Roo.log('listkeypress');
23562         // scroll to first matching element based on key pres..
23563         if (e.isSpecialKey()) {
23564             return false;
23565         }
23566         var k = String.fromCharCode(e.getKey()).toUpperCase();
23567         //Roo.log(k);
23568         var match  = false;
23569         var csel = this.view.getSelectedNodes();
23570         var cselitem = false;
23571         if (csel.length) {
23572             var ix = this.view.indexOf(csel[0]);
23573             cselitem  = this.store.getAt(ix);
23574             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23575                 cselitem = false;
23576             }
23577             
23578         }
23579         
23580         this.store.each(function(v) { 
23581             if (cselitem) {
23582                 // start at existing selection.
23583                 if (cselitem.id == v.id) {
23584                     cselitem = false;
23585                 }
23586                 return;
23587             }
23588                 
23589             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23590                 match = this.store.indexOf(v);
23591                 return false;
23592             }
23593         }, this);
23594         
23595         if (match === false) {
23596             return true; // no more action?
23597         }
23598         // scroll to?
23599         this.view.select(match);
23600         var sn = Roo.get(this.view.getSelectedNodes()[0])
23601         sn.scrollIntoView(sn.dom.parentNode, false);
23602     }
23603
23604     /** 
23605     * @cfg {Boolean} grow 
23606     * @hide 
23607     */
23608     /** 
23609     * @cfg {Number} growMin 
23610     * @hide 
23611     */
23612     /** 
23613     * @cfg {Number} growMax 
23614     * @hide 
23615     */
23616     /**
23617      * @hide
23618      * @method autoSize
23619      */
23620 });/*
23621  * Based on:
23622  * Ext JS Library 1.1.1
23623  * Copyright(c) 2006-2007, Ext JS, LLC.
23624  *
23625  * Originally Released Under LGPL - original licence link has changed is not relivant.
23626  *
23627  * Fork - LGPL
23628  * <script type="text/javascript">
23629  */
23630 /**
23631  * @class Roo.form.Checkbox
23632  * @extends Roo.form.Field
23633  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23634  * @constructor
23635  * Creates a new Checkbox
23636  * @param {Object} config Configuration options
23637  */
23638 Roo.form.Checkbox = function(config){
23639     Roo.form.Checkbox.superclass.constructor.call(this, config);
23640     this.addEvents({
23641         /**
23642          * @event check
23643          * Fires when the checkbox is checked or unchecked.
23644              * @param {Roo.form.Checkbox} this This checkbox
23645              * @param {Boolean} checked The new checked value
23646              */
23647         check : true
23648     });
23649 };
23650
23651 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23652     /**
23653      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23654      */
23655     focusClass : undefined,
23656     /**
23657      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23658      */
23659     fieldClass: "x-form-field",
23660     /**
23661      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23662      */
23663     checked: false,
23664     /**
23665      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23666      * {tag: "input", type: "checkbox", autocomplete: "off"})
23667      */
23668     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23669     /**
23670      * @cfg {String} boxLabel The text that appears beside the checkbox
23671      */
23672     boxLabel : "",
23673     /**
23674      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23675      */  
23676     inputValue : '1',
23677     /**
23678      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23679      */
23680      valueOff: '0', // value when not checked..
23681
23682     actionMode : 'viewEl', 
23683     //
23684     // private
23685     itemCls : 'x-menu-check-item x-form-item',
23686     groupClass : 'x-menu-group-item',
23687     inputType : 'hidden',
23688     
23689     
23690     inSetChecked: false, // check that we are not calling self...
23691     
23692     inputElement: false, // real input element?
23693     basedOn: false, // ????
23694     
23695     isFormField: true, // not sure where this is needed!!!!
23696
23697     onResize : function(){
23698         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23699         if(!this.boxLabel){
23700             this.el.alignTo(this.wrap, 'c-c');
23701         }
23702     },
23703
23704     initEvents : function(){
23705         Roo.form.Checkbox.superclass.initEvents.call(this);
23706         this.el.on("click", this.onClick,  this);
23707         this.el.on("change", this.onClick,  this);
23708     },
23709
23710
23711     getResizeEl : function(){
23712         return this.wrap;
23713     },
23714
23715     getPositionEl : function(){
23716         return this.wrap;
23717     },
23718
23719     // private
23720     onRender : function(ct, position){
23721         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23722         /*
23723         if(this.inputValue !== undefined){
23724             this.el.dom.value = this.inputValue;
23725         }
23726         */
23727         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23728         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23729         var viewEl = this.wrap.createChild({ 
23730             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23731         this.viewEl = viewEl;   
23732         this.wrap.on('click', this.onClick,  this); 
23733         
23734         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23735         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23736         
23737         
23738         
23739         if(this.boxLabel){
23740             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23741         //    viewEl.on('click', this.onClick,  this); 
23742         }
23743         //if(this.checked){
23744             this.setChecked(this.checked);
23745         //}else{
23746             //this.checked = this.el.dom;
23747         //}
23748
23749     },
23750
23751     // private
23752     initValue : Roo.emptyFn,
23753
23754     /**
23755      * Returns the checked state of the checkbox.
23756      * @return {Boolean} True if checked, else false
23757      */
23758     getValue : function(){
23759         if(this.el){
23760             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23761         }
23762         return this.valueOff;
23763         
23764     },
23765
23766         // private
23767     onClick : function(){ 
23768         this.setChecked(!this.checked);
23769
23770         //if(this.el.dom.checked != this.checked){
23771         //    this.setValue(this.el.dom.checked);
23772        // }
23773     },
23774
23775     /**
23776      * Sets the checked state of the checkbox.
23777      * On is always based on a string comparison between inputValue and the param.
23778      * @param {Boolean/String} value - the value to set 
23779      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23780      */
23781     setValue : function(v,suppressEvent){
23782         
23783         
23784         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23785         //if(this.el && this.el.dom){
23786         //    this.el.dom.checked = this.checked;
23787         //    this.el.dom.defaultChecked = this.checked;
23788         //}
23789         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23790         //this.fireEvent("check", this, this.checked);
23791     },
23792     // private..
23793     setChecked : function(state,suppressEvent)
23794     {
23795         if (this.inSetChecked) {
23796             this.checked = state;
23797             return;
23798         }
23799         
23800     
23801         if(this.wrap){
23802             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23803         }
23804         this.checked = state;
23805         if(suppressEvent !== true){
23806             this.fireEvent('check', this, state);
23807         }
23808         this.inSetChecked = true;
23809         this.el.dom.value = state ? this.inputValue : this.valueOff;
23810         this.inSetChecked = false;
23811         
23812     },
23813     // handle setting of hidden value by some other method!!?!?
23814     setFromHidden: function()
23815     {
23816         if(!this.el){
23817             return;
23818         }
23819         //console.log("SET FROM HIDDEN");
23820         //alert('setFrom hidden');
23821         this.setValue(this.el.dom.value);
23822     },
23823     
23824     onDestroy : function()
23825     {
23826         if(this.viewEl){
23827             Roo.get(this.viewEl).remove();
23828         }
23829          
23830         Roo.form.Checkbox.superclass.onDestroy.call(this);
23831     }
23832
23833 });/*
23834  * Based on:
23835  * Ext JS Library 1.1.1
23836  * Copyright(c) 2006-2007, Ext JS, LLC.
23837  *
23838  * Originally Released Under LGPL - original licence link has changed is not relivant.
23839  *
23840  * Fork - LGPL
23841  * <script type="text/javascript">
23842  */
23843  
23844 /**
23845  * @class Roo.form.Radio
23846  * @extends Roo.form.Checkbox
23847  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23848  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23849  * @constructor
23850  * Creates a new Radio
23851  * @param {Object} config Configuration options
23852  */
23853 Roo.form.Radio = function(){
23854     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23855 };
23856 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23857     inputType: 'radio',
23858
23859     /**
23860      * If this radio is part of a group, it will return the selected value
23861      * @return {String}
23862      */
23863     getGroupValue : function(){
23864         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23865     }
23866 });//<script type="text/javascript">
23867
23868 /*
23869  * Ext JS Library 1.1.1
23870  * Copyright(c) 2006-2007, Ext JS, LLC.
23871  * licensing@extjs.com
23872  * 
23873  * http://www.extjs.com/license
23874  */
23875  
23876  /*
23877   * 
23878   * Known bugs:
23879   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23880   * - IE ? - no idea how much works there.
23881   * 
23882   * 
23883   * 
23884   */
23885  
23886
23887 /**
23888  * @class Ext.form.HtmlEditor
23889  * @extends Ext.form.Field
23890  * Provides a lightweight HTML Editor component.
23891  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23892  * 
23893  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23894  * supported by this editor.</b><br/><br/>
23895  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23896  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23897  */
23898 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23899       /**
23900      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23901      */
23902     toolbars : false,
23903     /**
23904      * @cfg {String} createLinkText The default text for the create link prompt
23905      */
23906     createLinkText : 'Please enter the URL for the link:',
23907     /**
23908      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23909      */
23910     defaultLinkValue : 'http:/'+'/',
23911    
23912     
23913     // id of frame..
23914     frameId: false,
23915     
23916     // private properties
23917     validationEvent : false,
23918     deferHeight: true,
23919     initialized : false,
23920     activated : false,
23921     sourceEditMode : false,
23922     onFocus : Roo.emptyFn,
23923     iframePad:3,
23924     hideMode:'offsets',
23925     defaultAutoCreate : {
23926         tag: "textarea",
23927         style:"width:500px;height:300px;",
23928         autocomplete: "off"
23929     },
23930
23931     // private
23932     initComponent : function(){
23933         this.addEvents({
23934             /**
23935              * @event initialize
23936              * Fires when the editor is fully initialized (including the iframe)
23937              * @param {HtmlEditor} this
23938              */
23939             initialize: true,
23940             /**
23941              * @event activate
23942              * Fires when the editor is first receives the focus. Any insertion must wait
23943              * until after this event.
23944              * @param {HtmlEditor} this
23945              */
23946             activate: true,
23947              /**
23948              * @event beforesync
23949              * Fires before the textarea is updated with content from the editor iframe. Return false
23950              * to cancel the sync.
23951              * @param {HtmlEditor} this
23952              * @param {String} html
23953              */
23954             beforesync: true,
23955              /**
23956              * @event beforepush
23957              * Fires before the iframe editor is updated with content from the textarea. Return false
23958              * to cancel the push.
23959              * @param {HtmlEditor} this
23960              * @param {String} html
23961              */
23962             beforepush: true,
23963              /**
23964              * @event sync
23965              * Fires when the textarea is updated with content from the editor iframe.
23966              * @param {HtmlEditor} this
23967              * @param {String} html
23968              */
23969             sync: true,
23970              /**
23971              * @event push
23972              * Fires when the iframe editor is updated with content from the textarea.
23973              * @param {HtmlEditor} this
23974              * @param {String} html
23975              */
23976             push: true,
23977              /**
23978              * @event editmodechange
23979              * Fires when the editor switches edit modes
23980              * @param {HtmlEditor} this
23981              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23982              */
23983             editmodechange: true,
23984             /**
23985              * @event editorevent
23986              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23987              * @param {HtmlEditor} this
23988              */
23989             editorevent: true
23990         })
23991     },
23992
23993     /**
23994      * Protected method that will not generally be called directly. It
23995      * is called when the editor creates its toolbar. Override this method if you need to
23996      * add custom toolbar buttons.
23997      * @param {HtmlEditor} editor
23998      */
23999     createToolbar : function(editor){
24000         if (!editor.toolbars || !editor.toolbars.length) {
24001             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24002         }
24003         
24004         for (var i =0 ; i < editor.toolbars.length;i++) {
24005             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24006             editor.toolbars[i].init(editor);
24007         }
24008          
24009         
24010     },
24011
24012     /**
24013      * Protected method that will not generally be called directly. It
24014      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24015      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24016      */
24017     getDocMarkup : function(){
24018         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24019     },
24020
24021     // private
24022     onRender : function(ct, position){
24023         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24024         this.el.dom.style.border = '0 none';
24025         this.el.dom.setAttribute('tabIndex', -1);
24026         this.el.addClass('x-hidden');
24027         if(Roo.isIE){ // fix IE 1px bogus margin
24028             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24029         }
24030         this.wrap = this.el.wrap({
24031             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24032         });
24033
24034         this.frameId = Roo.id();
24035         this.createToolbar(this);
24036         
24037         
24038         
24039         
24040       
24041         
24042         var iframe = this.wrap.createChild({
24043             tag: 'iframe',
24044             id: this.frameId,
24045             name: this.frameId,
24046             frameBorder : 'no',
24047             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24048         });
24049         
24050        // console.log(iframe);
24051         //this.wrap.dom.appendChild(iframe);
24052
24053         this.iframe = iframe.dom;
24054
24055          this.assignDocWin();
24056         
24057         this.doc.designMode = 'on';
24058        
24059         this.doc.open();
24060         this.doc.write(this.getDocMarkup());
24061         this.doc.close();
24062
24063         
24064         var task = { // must defer to wait for browser to be ready
24065             run : function(){
24066                 //console.log("run task?" + this.doc.readyState);
24067                 this.assignDocWin();
24068                 if(this.doc.body || this.doc.readyState == 'complete'){
24069                     try {
24070                         this.doc.designMode="on";
24071                     } catch (e) {
24072                         return;
24073                     }
24074                     Roo.TaskMgr.stop(task);
24075                     this.initEditor.defer(10, this);
24076                 }
24077             },
24078             interval : 10,
24079             duration:10000,
24080             scope: this
24081         };
24082         Roo.TaskMgr.start(task);
24083
24084         if(!this.width){
24085             this.setSize(this.el.getSize());
24086         }
24087     },
24088
24089     // private
24090     onResize : function(w, h){
24091         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24092         if(this.el && this.iframe){
24093             if(typeof w == 'number'){
24094                 var aw = w - this.wrap.getFrameWidth('lr');
24095                 this.el.setWidth(this.adjustWidth('textarea', aw));
24096                 this.iframe.style.width = aw + 'px';
24097             }
24098             if(typeof h == 'number'){
24099                 var tbh = 0;
24100                 for (var i =0; i < this.toolbars.length;i++) {
24101                     // fixme - ask toolbars for heights?
24102                     tbh += this.toolbars[i].tb.el.getHeight();
24103                 }
24104                 
24105                 
24106                 
24107                 
24108                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24109                 this.el.setHeight(this.adjustWidth('textarea', ah));
24110                 this.iframe.style.height = ah + 'px';
24111                 if(this.doc){
24112                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24113                 }
24114             }
24115         }
24116     },
24117
24118     /**
24119      * Toggles the editor between standard and source edit mode.
24120      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24121      */
24122     toggleSourceEdit : function(sourceEditMode){
24123         
24124         this.sourceEditMode = sourceEditMode === true;
24125         
24126         if(this.sourceEditMode){
24127           
24128             this.syncValue();
24129             this.iframe.className = 'x-hidden';
24130             this.el.removeClass('x-hidden');
24131             this.el.dom.removeAttribute('tabIndex');
24132             this.el.focus();
24133         }else{
24134              
24135             this.pushValue();
24136             this.iframe.className = '';
24137             this.el.addClass('x-hidden');
24138             this.el.dom.setAttribute('tabIndex', -1);
24139             this.deferFocus();
24140         }
24141         this.setSize(this.wrap.getSize());
24142         this.fireEvent('editmodechange', this, this.sourceEditMode);
24143     },
24144
24145     // private used internally
24146     createLink : function(){
24147         var url = prompt(this.createLinkText, this.defaultLinkValue);
24148         if(url && url != 'http:/'+'/'){
24149             this.relayCmd('createlink', url);
24150         }
24151     },
24152
24153     // private (for BoxComponent)
24154     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24155
24156     // private (for BoxComponent)
24157     getResizeEl : function(){
24158         return this.wrap;
24159     },
24160
24161     // private (for BoxComponent)
24162     getPositionEl : function(){
24163         return this.wrap;
24164     },
24165
24166     // private
24167     initEvents : function(){
24168         this.originalValue = this.getValue();
24169     },
24170
24171     /**
24172      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24173      * @method
24174      */
24175     markInvalid : Roo.emptyFn,
24176     /**
24177      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24178      * @method
24179      */
24180     clearInvalid : Roo.emptyFn,
24181
24182     setValue : function(v){
24183         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24184         this.pushValue();
24185     },
24186
24187     /**
24188      * Protected method that will not generally be called directly. If you need/want
24189      * custom HTML cleanup, this is the method you should override.
24190      * @param {String} html The HTML to be cleaned
24191      * return {String} The cleaned HTML
24192      */
24193     cleanHtml : function(html){
24194         html = String(html);
24195         if(html.length > 5){
24196             if(Roo.isSafari){ // strip safari nonsense
24197                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24198             }
24199         }
24200         if(html == '&nbsp;'){
24201             html = '';
24202         }
24203         return html;
24204     },
24205
24206     /**
24207      * Protected method that will not generally be called directly. Syncs the contents
24208      * of the editor iframe with the textarea.
24209      */
24210     syncValue : function(){
24211         if(this.initialized){
24212             var bd = (this.doc.body || this.doc.documentElement);
24213             var html = bd.innerHTML;
24214             if(Roo.isSafari){
24215                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24216                 var m = bs.match(/text-align:(.*?);/i);
24217                 if(m && m[1]){
24218                     html = '<div style="'+m[0]+'">' + html + '</div>';
24219                 }
24220             }
24221             html = this.cleanHtml(html);
24222             if(this.fireEvent('beforesync', this, html) !== false){
24223                 this.el.dom.value = html;
24224                 this.fireEvent('sync', this, html);
24225             }
24226         }
24227     },
24228
24229     /**
24230      * Protected method that will not generally be called directly. Pushes the value of the textarea
24231      * into the iframe editor.
24232      */
24233     pushValue : function(){
24234         if(this.initialized){
24235             var v = this.el.dom.value;
24236             if(v.length < 1){
24237                 v = '&#160;';
24238             }
24239             if(this.fireEvent('beforepush', this, v) !== false){
24240                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24241                 this.fireEvent('push', this, v);
24242             }
24243         }
24244     },
24245
24246     // private
24247     deferFocus : function(){
24248         this.focus.defer(10, this);
24249     },
24250
24251     // doc'ed in Field
24252     focus : function(){
24253         if(this.win && !this.sourceEditMode){
24254             this.win.focus();
24255         }else{
24256             this.el.focus();
24257         }
24258     },
24259     
24260     assignDocWin: function()
24261     {
24262         var iframe = this.iframe;
24263         
24264          if(Roo.isIE){
24265             this.doc = iframe.contentWindow.document;
24266             this.win = iframe.contentWindow;
24267         } else {
24268             if (!Roo.get(this.frameId)) {
24269                 return;
24270             }
24271             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24272             this.win = Roo.get(this.frameId).dom.contentWindow;
24273         }
24274     },
24275     
24276     // private
24277     initEditor : function(){
24278         //console.log("INIT EDITOR");
24279         this.assignDocWin();
24280         
24281         
24282         
24283         this.doc.designMode="on";
24284         this.doc.open();
24285         this.doc.write(this.getDocMarkup());
24286         this.doc.close();
24287         
24288         var dbody = (this.doc.body || this.doc.documentElement);
24289         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24290         // this copies styles from the containing element into thsi one..
24291         // not sure why we need all of this..
24292         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24293         ss['background-attachment'] = 'fixed'; // w3c
24294         dbody.bgProperties = 'fixed'; // ie
24295         Roo.DomHelper.applyStyles(dbody, ss);
24296         Roo.EventManager.on(this.doc, {
24297             'mousedown': this.onEditorEvent,
24298             'dblclick': this.onEditorEvent,
24299             'click': this.onEditorEvent,
24300             'keyup': this.onEditorEvent,
24301             buffer:100,
24302             scope: this
24303         });
24304         if(Roo.isGecko){
24305             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24306         }
24307         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24308             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24309         }
24310         this.initialized = true;
24311
24312         this.fireEvent('initialize', this);
24313         this.pushValue();
24314     },
24315
24316     // private
24317     onDestroy : function(){
24318         
24319         
24320         
24321         if(this.rendered){
24322             
24323             for (var i =0; i < this.toolbars.length;i++) {
24324                 // fixme - ask toolbars for heights?
24325                 this.toolbars[i].onDestroy();
24326             }
24327             
24328             this.wrap.dom.innerHTML = '';
24329             this.wrap.remove();
24330         }
24331     },
24332
24333     // private
24334     onFirstFocus : function(){
24335         
24336         this.assignDocWin();
24337         
24338         
24339         this.activated = true;
24340         for (var i =0; i < this.toolbars.length;i++) {
24341             this.toolbars[i].onFirstFocus();
24342         }
24343        
24344         if(Roo.isGecko){ // prevent silly gecko errors
24345             this.win.focus();
24346             var s = this.win.getSelection();
24347             if(!s.focusNode || s.focusNode.nodeType != 3){
24348                 var r = s.getRangeAt(0);
24349                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24350                 r.collapse(true);
24351                 this.deferFocus();
24352             }
24353             try{
24354                 this.execCmd('useCSS', true);
24355                 this.execCmd('styleWithCSS', false);
24356             }catch(e){}
24357         }
24358         this.fireEvent('activate', this);
24359     },
24360
24361     // private
24362     adjustFont: function(btn){
24363         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24364         //if(Roo.isSafari){ // safari
24365         //    adjust *= 2;
24366        // }
24367         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24368         if(Roo.isSafari){ // safari
24369             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24370             v =  (v < 10) ? 10 : v;
24371             v =  (v > 48) ? 48 : v;
24372             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24373             
24374         }
24375         
24376         
24377         v = Math.max(1, v+adjust);
24378         
24379         this.execCmd('FontSize', v  );
24380     },
24381
24382     onEditorEvent : function(e){
24383         this.fireEvent('editorevent', this, e);
24384       //  this.updateToolbar();
24385         this.syncValue();
24386     },
24387
24388     insertTag : function(tg)
24389     {
24390         // could be a bit smarter... -> wrap the current selected tRoo..
24391         
24392         this.execCmd("formatblock",   tg);
24393         
24394     },
24395     
24396     insertText : function(txt)
24397     {
24398         
24399         
24400         range = this.createRange();
24401         range.deleteContents();
24402                //alert(Sender.getAttribute('label'));
24403                
24404         range.insertNode(this.doc.createTextNode(txt));
24405     } ,
24406     
24407     // private
24408     relayBtnCmd : function(btn){
24409         this.relayCmd(btn.cmd);
24410     },
24411
24412     /**
24413      * Executes a Midas editor command on the editor document and performs necessary focus and
24414      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24415      * @param {String} cmd The Midas command
24416      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24417      */
24418     relayCmd : function(cmd, value){
24419         this.win.focus();
24420         this.execCmd(cmd, value);
24421         this.fireEvent('editorevent', this);
24422         //this.updateToolbar();
24423         this.deferFocus();
24424     },
24425
24426     /**
24427      * Executes a Midas editor command directly on the editor document.
24428      * For visual commands, you should use {@link #relayCmd} instead.
24429      * <b>This should only be called after the editor is initialized.</b>
24430      * @param {String} cmd The Midas command
24431      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24432      */
24433     execCmd : function(cmd, value){
24434         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24435         this.syncValue();
24436     },
24437
24438    
24439     /**
24440      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24441      * to insert tRoo.
24442      * @param {String} text
24443      */
24444     insertAtCursor : function(text){
24445         if(!this.activated){
24446             return;
24447         }
24448         if(Roo.isIE){
24449             this.win.focus();
24450             var r = this.doc.selection.createRange();
24451             if(r){
24452                 r.collapse(true);
24453                 r.pasteHTML(text);
24454                 this.syncValue();
24455                 this.deferFocus();
24456             }
24457         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24458             this.win.focus();
24459             this.execCmd('InsertHTML', text);
24460             this.deferFocus();
24461         }
24462     },
24463  // private
24464     mozKeyPress : function(e){
24465         if(e.ctrlKey){
24466             var c = e.getCharCode(), cmd;
24467           
24468             if(c > 0){
24469                 c = String.fromCharCode(c).toLowerCase();
24470                 switch(c){
24471                     case 'b':
24472                         cmd = 'bold';
24473                     break;
24474                     case 'i':
24475                         cmd = 'italic';
24476                     break;
24477                     case 'u':
24478                         cmd = 'underline';
24479                     case 'v':
24480                         this.cleanUpPaste.defer(100, this);
24481                         return;
24482                     break;
24483                 }
24484                 if(cmd){
24485                     this.win.focus();
24486                     this.execCmd(cmd);
24487                     this.deferFocus();
24488                     e.preventDefault();
24489                 }
24490                 
24491             }
24492         }
24493     },
24494
24495     // private
24496     fixKeys : function(){ // load time branching for fastest keydown performance
24497         if(Roo.isIE){
24498             return function(e){
24499                 var k = e.getKey(), r;
24500                 if(k == e.TAB){
24501                     e.stopEvent();
24502                     r = this.doc.selection.createRange();
24503                     if(r){
24504                         r.collapse(true);
24505                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24506                         this.deferFocus();
24507                     }
24508                     return;
24509                 }
24510                 
24511                 if(k == e.ENTER){
24512                     r = this.doc.selection.createRange();
24513                     if(r){
24514                         var target = r.parentElement();
24515                         if(!target || target.tagName.toLowerCase() != 'li'){
24516                             e.stopEvent();
24517                             r.pasteHTML('<br />');
24518                             r.collapse(false);
24519                             r.select();
24520                         }
24521                     }
24522                 }
24523                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24524                     this.cleanUpPaste.defer(100, this);
24525                     return;
24526                 }
24527                 
24528                 
24529             };
24530         }else if(Roo.isOpera){
24531             return function(e){
24532                 var k = e.getKey();
24533                 if(k == e.TAB){
24534                     e.stopEvent();
24535                     this.win.focus();
24536                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24537                     this.deferFocus();
24538                 }
24539                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24540                     this.cleanUpPaste.defer(100, this);
24541                     return;
24542                 }
24543                 
24544             };
24545         }else if(Roo.isSafari){
24546             return function(e){
24547                 var k = e.getKey();
24548                 
24549                 if(k == e.TAB){
24550                     e.stopEvent();
24551                     this.execCmd('InsertText','\t');
24552                     this.deferFocus();
24553                     return;
24554                 }
24555                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24556                     this.cleanUpPaste.defer(100, this);
24557                     return;
24558                 }
24559                 
24560              };
24561         }
24562     }(),
24563     
24564     getAllAncestors: function()
24565     {
24566         var p = this.getSelectedNode();
24567         var a = [];
24568         if (!p) {
24569             a.push(p); // push blank onto stack..
24570             p = this.getParentElement();
24571         }
24572         
24573         
24574         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24575             a.push(p);
24576             p = p.parentNode;
24577         }
24578         a.push(this.doc.body);
24579         return a;
24580     },
24581     lastSel : false,
24582     lastSelNode : false,
24583     
24584     
24585     getSelection : function() 
24586     {
24587         this.assignDocWin();
24588         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24589     },
24590     
24591     getSelectedNode: function() 
24592     {
24593         // this may only work on Gecko!!!
24594         
24595         // should we cache this!!!!
24596         
24597         
24598         
24599          
24600         var range = this.createRange(this.getSelection());
24601         
24602         if (Roo.isIE) {
24603             var parent = range.parentElement();
24604             while (true) {
24605                 var testRange = range.duplicate();
24606                 testRange.moveToElementText(parent);
24607                 if (testRange.inRange(range)) {
24608                     break;
24609                 }
24610                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24611                     break;
24612                 }
24613                 parent = parent.parentElement;
24614             }
24615             return parent;
24616         }
24617         
24618         
24619         var ar = range.endContainer.childNodes;
24620         if (!ar.length) {
24621             ar = range.commonAncestorContainer.childNodes;
24622             //alert(ar.length);
24623         }
24624         var nodes = [];
24625         var other_nodes = [];
24626         var has_other_nodes = false;
24627         for (var i=0;i<ar.length;i++) {
24628             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24629                 continue;
24630             }
24631             // fullly contained node.
24632             
24633             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24634                 nodes.push(ar[i]);
24635                 continue;
24636             }
24637             
24638             // probably selected..
24639             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24640                 other_nodes.push(ar[i]);
24641                 continue;
24642             }
24643             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24644                 continue;
24645             }
24646             
24647             
24648             has_other_nodes = true;
24649         }
24650         if (!nodes.length && other_nodes.length) {
24651             nodes= other_nodes;
24652         }
24653         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24654             return false;
24655         }
24656         
24657         return nodes[0];
24658     },
24659     createRange: function(sel)
24660     {
24661         // this has strange effects when using with 
24662         // top toolbar - not sure if it's a great idea.
24663         //this.editor.contentWindow.focus();
24664         if (typeof sel != "undefined") {
24665             try {
24666                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24667             } catch(e) {
24668                 return this.doc.createRange();
24669             }
24670         } else {
24671             return this.doc.createRange();
24672         }
24673     },
24674     getParentElement: function()
24675     {
24676         
24677         this.assignDocWin();
24678         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24679         
24680         var range = this.createRange(sel);
24681          
24682         try {
24683             var p = range.commonAncestorContainer;
24684             while (p.nodeType == 3) { // text node
24685                 p = p.parentNode;
24686             }
24687             return p;
24688         } catch (e) {
24689             return null;
24690         }
24691     
24692     },
24693     
24694     
24695     
24696     // BC Hacks - cause I cant work out what i was trying to do..
24697     rangeIntersectsNode : function(range, node)
24698     {
24699         var nodeRange = node.ownerDocument.createRange();
24700         try {
24701             nodeRange.selectNode(node);
24702         }
24703         catch (e) {
24704             nodeRange.selectNodeContents(node);
24705         }
24706
24707         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24708                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24709     },
24710     rangeCompareNode : function(range, node) {
24711         var nodeRange = node.ownerDocument.createRange();
24712         try {
24713             nodeRange.selectNode(node);
24714         } catch (e) {
24715             nodeRange.selectNodeContents(node);
24716         }
24717         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24718         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24719
24720         if (nodeIsBefore && !nodeIsAfter)
24721             return 0;
24722         if (!nodeIsBefore && nodeIsAfter)
24723             return 1;
24724         if (nodeIsBefore && nodeIsAfter)
24725             return 2;
24726
24727         return 3;
24728     },
24729
24730     // private? - in a new class?
24731     cleanUpPaste :  function()
24732     {
24733         // cleans up the whole document..
24734       //  console.log('cleanuppaste');
24735         this.cleanUpChildren(this.doc.body)
24736         
24737         
24738     },
24739     cleanUpChildren : function (n)
24740     {
24741         if (!n.childNodes.length) {
24742             return;
24743         }
24744         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24745            this.cleanUpChild(n.childNodes[i]);
24746         }
24747     },
24748     
24749     
24750         
24751     
24752     cleanUpChild : function (node)
24753     {
24754         //console.log(node);
24755         if (node.nodeName == "#text") {
24756             // clean up silly Windows -- stuff?
24757             return; 
24758         }
24759         if (node.nodeName == "#comment") {
24760             node.parentNode.removeChild(node);
24761             // clean up silly Windows -- stuff?
24762             return; 
24763         }
24764         
24765         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24766             // remove node.
24767             node.parentNode.removeChild(node);
24768             return;
24769             
24770         }
24771         if (!node.attributes || !node.attributes.length) {
24772             this.cleanUpChildren(node);
24773             return;
24774         }
24775         
24776         function cleanAttr(n,v)
24777         {
24778             
24779             if (v.match(/^\./) || v.match(/^\//)) {
24780                 return;
24781             }
24782             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24783                 return;
24784             }
24785             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24786             node.removeAttribute(n);
24787             
24788         }
24789         
24790         function cleanStyle(n,v)
24791         {
24792             if (v.match(/expression/)) { //XSS?? should we even bother..
24793                 node.removeAttribute(n);
24794                 return;
24795             }
24796             
24797             
24798             var parts = v.split(/;/);
24799             Roo.each(parts, function(p) {
24800                 p = p.replace(/\s+/g,'');
24801                 if (!p.length) {
24802                     return;
24803                 }
24804                 var l = p.split(':').shift().replace(/\s+/g,'');
24805                 
24806                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24807                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24808                     node.removeAttribute(n);
24809                     return false;
24810                 }
24811             });
24812             
24813             
24814         }
24815         
24816         
24817         for (var i = node.attributes.length-1; i > -1 ; i--) {
24818             var a = node.attributes[i];
24819             //console.log(a);
24820             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24821                 node.removeAttribute(a.name);
24822                 return;
24823             }
24824             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24825                 cleanAttr(a.name,a.value); // fixme..
24826                 return;
24827             }
24828             if (a.name == 'style') {
24829                 cleanStyle(a.name,a.value);
24830             }
24831             /// clean up MS crap..
24832             if (a.name == 'class') {
24833                 if (a.value.match(/^Mso/)) {
24834                     node.className = '';
24835                 }
24836             }
24837             
24838             // style cleanup!?
24839             // class cleanup?
24840             
24841         }
24842         
24843         
24844         this.cleanUpChildren(node);
24845         
24846         
24847     }
24848     
24849     
24850     // hide stuff that is not compatible
24851     /**
24852      * @event blur
24853      * @hide
24854      */
24855     /**
24856      * @event change
24857      * @hide
24858      */
24859     /**
24860      * @event focus
24861      * @hide
24862      */
24863     /**
24864      * @event specialkey
24865      * @hide
24866      */
24867     /**
24868      * @cfg {String} fieldClass @hide
24869      */
24870     /**
24871      * @cfg {String} focusClass @hide
24872      */
24873     /**
24874      * @cfg {String} autoCreate @hide
24875      */
24876     /**
24877      * @cfg {String} inputType @hide
24878      */
24879     /**
24880      * @cfg {String} invalidClass @hide
24881      */
24882     /**
24883      * @cfg {String} invalidText @hide
24884      */
24885     /**
24886      * @cfg {String} msgFx @hide
24887      */
24888     /**
24889      * @cfg {String} validateOnBlur @hide
24890      */
24891 });
24892
24893 Roo.form.HtmlEditor.white = [
24894         'area', 'br', 'img', 'input', 'hr', 'wbr',
24895         
24896        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24897        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24898        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24899        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24900        'table',   'ul',         'xmp', 
24901        
24902        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24903       'thead',   'tr', 
24904      
24905       'dir', 'menu', 'ol', 'ul', 'dl',
24906        
24907       'embed',  'object'
24908 ];
24909
24910
24911 Roo.form.HtmlEditor.black = [
24912     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24913         'applet', // 
24914         'base',   'basefont', 'bgsound', 'blink',  'body', 
24915         'frame',  'frameset', 'head',    'html',   'ilayer', 
24916         'iframe', 'layer',  'link',     'meta',    'object',   
24917         'script', 'style' ,'title',  'xml' // clean later..
24918 ];
24919 Roo.form.HtmlEditor.clean = [
24920     'script', 'style', 'title', 'xml'
24921 ];
24922
24923 // attributes..
24924
24925 Roo.form.HtmlEditor.ablack = [
24926     'on'
24927 ];
24928     
24929 Roo.form.HtmlEditor.aclean = [ 
24930     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24931 ];
24932
24933 // protocols..
24934 Roo.form.HtmlEditor.pwhite= [
24935         'http',  'https',  'mailto'
24936 ];
24937
24938 Roo.form.HtmlEditor.cwhite= [
24939         'text-align',
24940         'font-size'
24941 ];
24942
24943 // <script type="text/javascript">
24944 /*
24945  * Based on
24946  * Ext JS Library 1.1.1
24947  * Copyright(c) 2006-2007, Ext JS, LLC.
24948  *  
24949  
24950  */
24951
24952 /**
24953  * @class Roo.form.HtmlEditorToolbar1
24954  * Basic Toolbar
24955  * 
24956  * Usage:
24957  *
24958  new Roo.form.HtmlEditor({
24959     ....
24960     toolbars : [
24961         new Roo.form.HtmlEditorToolbar1({
24962             disable : { fonts: 1 , format: 1, ..., ... , ...],
24963             btns : [ .... ]
24964         })
24965     }
24966      
24967  * 
24968  * @cfg {Object} disable List of elements to disable..
24969  * @cfg {Array} btns List of additional buttons.
24970  * 
24971  * 
24972  * NEEDS Extra CSS? 
24973  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24974  */
24975  
24976 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24977 {
24978     
24979     Roo.apply(this, config);
24980     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24981     // dont call parent... till later.
24982 }
24983
24984 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24985     
24986     tb: false,
24987     
24988     rendered: false,
24989     
24990     editor : false,
24991     /**
24992      * @cfg {Object} disable  List of toolbar elements to disable
24993          
24994      */
24995     disable : false,
24996       /**
24997      * @cfg {Array} fontFamilies An array of available font families
24998      */
24999     fontFamilies : [
25000         'Arial',
25001         'Courier New',
25002         'Tahoma',
25003         'Times New Roman',
25004         'Verdana'
25005     ],
25006     
25007     specialChars : [
25008            "&#169;",
25009           "&#174;",     
25010           "&#8482;",    
25011           "&#163;" ,    
25012          // "&#8212;",    
25013           "&#8230;",    
25014           "&#247;" ,    
25015         //  "&#225;" ,     ?? a acute?
25016            "&#8364;"    , //Euro
25017        //   "&#8220;"    ,
25018         //  "&#8221;"    ,
25019         //  "&#8226;"    ,
25020           "&#176;"  //   , // degrees
25021
25022          // "&#233;"     , // e ecute
25023          // "&#250;"     , // u ecute?
25024     ],
25025     inputElements : [ 
25026             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25027             "input:submit", "input:button", "select", "textarea", "label" ],
25028     formats : [
25029         ["p"] ,  
25030         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25031         ["pre"],[ "code"], 
25032         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25033     ],
25034      /**
25035      * @cfg {String} defaultFont default font to use.
25036      */
25037     defaultFont: 'tahoma',
25038    
25039     fontSelect : false,
25040     
25041     
25042     formatCombo : false,
25043     
25044     init : function(editor)
25045     {
25046         this.editor = editor;
25047         
25048         
25049         var fid = editor.frameId;
25050         var etb = this;
25051         function btn(id, toggle, handler){
25052             var xid = fid + '-'+ id ;
25053             return {
25054                 id : xid,
25055                 cmd : id,
25056                 cls : 'x-btn-icon x-edit-'+id,
25057                 enableToggle:toggle !== false,
25058                 scope: editor, // was editor...
25059                 handler:handler||editor.relayBtnCmd,
25060                 clickEvent:'mousedown',
25061                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25062                 tabIndex:-1
25063             };
25064         }
25065         
25066         
25067         
25068         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25069         this.tb = tb;
25070          // stop form submits
25071         tb.el.on('click', function(e){
25072             e.preventDefault(); // what does this do?
25073         });
25074
25075         if(!this.disable.font && !Roo.isSafari){
25076             /* why no safari for fonts
25077             editor.fontSelect = tb.el.createChild({
25078                 tag:'select',
25079                 tabIndex: -1,
25080                 cls:'x-font-select',
25081                 html: editor.createFontOptions()
25082             });
25083             editor.fontSelect.on('change', function(){
25084                 var font = editor.fontSelect.dom.value;
25085                 editor.relayCmd('fontname', font);
25086                 editor.deferFocus();
25087             }, editor);
25088             tb.add(
25089                 editor.fontSelect.dom,
25090                 '-'
25091             );
25092             */
25093         };
25094         if(!this.disable.formats){
25095             this.formatCombo = new Roo.form.ComboBox({
25096                 store: new Roo.data.SimpleStore({
25097                     id : 'tag',
25098                     fields: ['tag'],
25099                     data : this.formats // from states.js
25100                 }),
25101                 blockFocus : true,
25102                 //autoCreate : {tag: "div",  size: "20"},
25103                 displayField:'tag',
25104                 typeAhead: false,
25105                 mode: 'local',
25106                 editable : false,
25107                 triggerAction: 'all',
25108                 emptyText:'Add tag',
25109                 selectOnFocus:true,
25110                 width:135,
25111                 listeners : {
25112                     'select': function(c, r, i) {
25113                         editor.insertTag(r.get('tag'));
25114                         editor.focus();
25115                     }
25116                 }
25117
25118             });
25119             tb.addField(this.formatCombo);
25120             
25121         }
25122         
25123         if(!this.disable.format){
25124             tb.add(
25125                 btn('bold'),
25126                 btn('italic'),
25127                 btn('underline')
25128             );
25129         };
25130         if(!this.disable.fontSize){
25131             tb.add(
25132                 '-',
25133                 
25134                 
25135                 btn('increasefontsize', false, editor.adjustFont),
25136                 btn('decreasefontsize', false, editor.adjustFont)
25137             );
25138         };
25139         
25140         
25141         if(this.disable.colors){
25142             tb.add(
25143                 '-', {
25144                     id:editor.frameId +'-forecolor',
25145                     cls:'x-btn-icon x-edit-forecolor',
25146                     clickEvent:'mousedown',
25147                     tooltip: this.buttonTips['forecolor'] || undefined,
25148                     tabIndex:-1,
25149                     menu : new Roo.menu.ColorMenu({
25150                         allowReselect: true,
25151                         focus: Roo.emptyFn,
25152                         value:'000000',
25153                         plain:true,
25154                         selectHandler: function(cp, color){
25155                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25156                             editor.deferFocus();
25157                         },
25158                         scope: editor,
25159                         clickEvent:'mousedown'
25160                     })
25161                 }, {
25162                     id:editor.frameId +'backcolor',
25163                     cls:'x-btn-icon x-edit-backcolor',
25164                     clickEvent:'mousedown',
25165                     tooltip: this.buttonTips['backcolor'] || undefined,
25166                     tabIndex:-1,
25167                     menu : new Roo.menu.ColorMenu({
25168                         focus: Roo.emptyFn,
25169                         value:'FFFFFF',
25170                         plain:true,
25171                         allowReselect: true,
25172                         selectHandler: function(cp, color){
25173                             if(Roo.isGecko){
25174                                 editor.execCmd('useCSS', false);
25175                                 editor.execCmd('hilitecolor', color);
25176                                 editor.execCmd('useCSS', true);
25177                                 editor.deferFocus();
25178                             }else{
25179                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25180                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25181                                 editor.deferFocus();
25182                             }
25183                         },
25184                         scope:editor,
25185                         clickEvent:'mousedown'
25186                     })
25187                 }
25188             );
25189         };
25190         // now add all the items...
25191         
25192
25193         if(!this.disable.alignments){
25194             tb.add(
25195                 '-',
25196                 btn('justifyleft'),
25197                 btn('justifycenter'),
25198                 btn('justifyright')
25199             );
25200         };
25201
25202         //if(!Roo.isSafari){
25203             if(!this.disable.links){
25204                 tb.add(
25205                     '-',
25206                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25207                 );
25208             };
25209
25210             if(!this.disable.lists){
25211                 tb.add(
25212                     '-',
25213                     btn('insertorderedlist'),
25214                     btn('insertunorderedlist')
25215                 );
25216             }
25217             if(!this.disable.sourceEdit){
25218                 tb.add(
25219                     '-',
25220                     btn('sourceedit', true, function(btn){
25221                         this.toggleSourceEdit(btn.pressed);
25222                     })
25223                 );
25224             }
25225         //}
25226         
25227         var smenu = { };
25228         // special menu.. - needs to be tidied up..
25229         if (!this.disable.special) {
25230             smenu = {
25231                 text: "&#169;",
25232                 cls: 'x-edit-none',
25233                 menu : {
25234                     items : []
25235                    }
25236             };
25237             for (var i =0; i < this.specialChars.length; i++) {
25238                 smenu.menu.items.push({
25239                     
25240                     html: this.specialChars[i],
25241                     handler: function(a,b) {
25242                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25243                         
25244                     },
25245                     tabIndex:-1
25246                 });
25247             }
25248             
25249             
25250             tb.add(smenu);
25251             
25252             
25253         }
25254         if (this.btns) {
25255             for(var i =0; i< this.btns.length;i++) {
25256                 var b = this.btns[i];
25257                 b.cls =  'x-edit-none';
25258                 b.scope = editor;
25259                 tb.add(b);
25260             }
25261         
25262         }
25263         
25264         
25265         
25266         // disable everything...
25267         
25268         this.tb.items.each(function(item){
25269            if(item.id != editor.frameId+ '-sourceedit'){
25270                 item.disable();
25271             }
25272         });
25273         this.rendered = true;
25274         
25275         // the all the btns;
25276         editor.on('editorevent', this.updateToolbar, this);
25277         // other toolbars need to implement this..
25278         //editor.on('editmodechange', this.updateToolbar, this);
25279     },
25280     
25281     
25282     
25283     /**
25284      * Protected method that will not generally be called directly. It triggers
25285      * a toolbar update by reading the markup state of the current selection in the editor.
25286      */
25287     updateToolbar: function(){
25288
25289         if(!this.editor.activated){
25290             this.editor.onFirstFocus();
25291             return;
25292         }
25293
25294         var btns = this.tb.items.map, 
25295             doc = this.editor.doc,
25296             frameId = this.editor.frameId;
25297
25298         if(!this.disable.font && !Roo.isSafari){
25299             /*
25300             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25301             if(name != this.fontSelect.dom.value){
25302                 this.fontSelect.dom.value = name;
25303             }
25304             */
25305         }
25306         if(!this.disable.format){
25307             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25308             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25309             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25310         }
25311         if(!this.disable.alignments){
25312             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25313             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25314             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25315         }
25316         if(!Roo.isSafari && !this.disable.lists){
25317             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25318             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25319         }
25320         
25321         var ans = this.editor.getAllAncestors();
25322         if (this.formatCombo) {
25323             
25324             
25325             var store = this.formatCombo.store;
25326             this.formatCombo.setValue("");
25327             for (var i =0; i < ans.length;i++) {
25328                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25329                     // select it..
25330                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25331                     break;
25332                 }
25333             }
25334         }
25335         
25336         
25337         
25338         // hides menus... - so this cant be on a menu...
25339         Roo.menu.MenuMgr.hideAll();
25340
25341         //this.editorsyncValue();
25342     },
25343    
25344     
25345     createFontOptions : function(){
25346         var buf = [], fs = this.fontFamilies, ff, lc;
25347         for(var i = 0, len = fs.length; i< len; i++){
25348             ff = fs[i];
25349             lc = ff.toLowerCase();
25350             buf.push(
25351                 '<option value="',lc,'" style="font-family:',ff,';"',
25352                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25353                     ff,
25354                 '</option>'
25355             );
25356         }
25357         return buf.join('');
25358     },
25359     
25360     toggleSourceEdit : function(sourceEditMode){
25361         if(sourceEditMode === undefined){
25362             sourceEditMode = !this.sourceEditMode;
25363         }
25364         this.sourceEditMode = sourceEditMode === true;
25365         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25366         // just toggle the button?
25367         if(btn.pressed !== this.editor.sourceEditMode){
25368             btn.toggle(this.editor.sourceEditMode);
25369             return;
25370         }
25371         
25372         if(this.sourceEditMode){
25373             this.tb.items.each(function(item){
25374                 if(item.cmd != 'sourceedit'){
25375                     item.disable();
25376                 }
25377             });
25378           
25379         }else{
25380             if(this.initialized){
25381                 this.tb.items.each(function(item){
25382                     item.enable();
25383                 });
25384             }
25385             
25386         }
25387         // tell the editor that it's been pressed..
25388         this.editor.toggleSourceEdit(sourceEditMode);
25389        
25390     },
25391      /**
25392      * Object collection of toolbar tooltips for the buttons in the editor. The key
25393      * is the command id associated with that button and the value is a valid QuickTips object.
25394      * For example:
25395 <pre><code>
25396 {
25397     bold : {
25398         title: 'Bold (Ctrl+B)',
25399         text: 'Make the selected text bold.',
25400         cls: 'x-html-editor-tip'
25401     },
25402     italic : {
25403         title: 'Italic (Ctrl+I)',
25404         text: 'Make the selected text italic.',
25405         cls: 'x-html-editor-tip'
25406     },
25407     ...
25408 </code></pre>
25409     * @type Object
25410      */
25411     buttonTips : {
25412         bold : {
25413             title: 'Bold (Ctrl+B)',
25414             text: 'Make the selected text bold.',
25415             cls: 'x-html-editor-tip'
25416         },
25417         italic : {
25418             title: 'Italic (Ctrl+I)',
25419             text: 'Make the selected text italic.',
25420             cls: 'x-html-editor-tip'
25421         },
25422         underline : {
25423             title: 'Underline (Ctrl+U)',
25424             text: 'Underline the selected text.',
25425             cls: 'x-html-editor-tip'
25426         },
25427         increasefontsize : {
25428             title: 'Grow Text',
25429             text: 'Increase the font size.',
25430             cls: 'x-html-editor-tip'
25431         },
25432         decreasefontsize : {
25433             title: 'Shrink Text',
25434             text: 'Decrease the font size.',
25435             cls: 'x-html-editor-tip'
25436         },
25437         backcolor : {
25438             title: 'Text Highlight Color',
25439             text: 'Change the background color of the selected text.',
25440             cls: 'x-html-editor-tip'
25441         },
25442         forecolor : {
25443             title: 'Font Color',
25444             text: 'Change the color of the selected text.',
25445             cls: 'x-html-editor-tip'
25446         },
25447         justifyleft : {
25448             title: 'Align Text Left',
25449             text: 'Align text to the left.',
25450             cls: 'x-html-editor-tip'
25451         },
25452         justifycenter : {
25453             title: 'Center Text',
25454             text: 'Center text in the editor.',
25455             cls: 'x-html-editor-tip'
25456         },
25457         justifyright : {
25458             title: 'Align Text Right',
25459             text: 'Align text to the right.',
25460             cls: 'x-html-editor-tip'
25461         },
25462         insertunorderedlist : {
25463             title: 'Bullet List',
25464             text: 'Start a bulleted list.',
25465             cls: 'x-html-editor-tip'
25466         },
25467         insertorderedlist : {
25468             title: 'Numbered List',
25469             text: 'Start a numbered list.',
25470             cls: 'x-html-editor-tip'
25471         },
25472         createlink : {
25473             title: 'Hyperlink',
25474             text: 'Make the selected text a hyperlink.',
25475             cls: 'x-html-editor-tip'
25476         },
25477         sourceedit : {
25478             title: 'Source Edit',
25479             text: 'Switch to source editing mode.',
25480             cls: 'x-html-editor-tip'
25481         }
25482     },
25483     // private
25484     onDestroy : function(){
25485         if(this.rendered){
25486             
25487             this.tb.items.each(function(item){
25488                 if(item.menu){
25489                     item.menu.removeAll();
25490                     if(item.menu.el){
25491                         item.menu.el.destroy();
25492                     }
25493                 }
25494                 item.destroy();
25495             });
25496              
25497         }
25498     },
25499     onFirstFocus: function() {
25500         this.tb.items.each(function(item){
25501            item.enable();
25502         });
25503     }
25504 });
25505
25506
25507
25508
25509 // <script type="text/javascript">
25510 /*
25511  * Based on
25512  * Ext JS Library 1.1.1
25513  * Copyright(c) 2006-2007, Ext JS, LLC.
25514  *  
25515  
25516  */
25517
25518  
25519 /**
25520  * @class Roo.form.HtmlEditor.ToolbarContext
25521  * Context Toolbar
25522  * 
25523  * Usage:
25524  *
25525  new Roo.form.HtmlEditor({
25526     ....
25527     toolbars : [
25528         new Roo.form.HtmlEditor.ToolbarStandard(),
25529         new Roo.form.HtmlEditor.ToolbarContext()
25530         })
25531     }
25532      
25533  * 
25534  * @config : {Object} disable List of elements to disable.. (not done yet.)
25535  * 
25536  * 
25537  */
25538
25539 Roo.form.HtmlEditor.ToolbarContext = function(config)
25540 {
25541     
25542     Roo.apply(this, config);
25543     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25544     // dont call parent... till later.
25545 }
25546 Roo.form.HtmlEditor.ToolbarContext.types = {
25547     'IMG' : {
25548         width : {
25549             title: "Width",
25550             width: 40
25551         },
25552         height:  {
25553             title: "Height",
25554             width: 40
25555         },
25556         align: {
25557             title: "Align",
25558             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25559             width : 80
25560             
25561         },
25562         border: {
25563             title: "Border",
25564             width: 40
25565         },
25566         alt: {
25567             title: "Alt",
25568             width: 120
25569         },
25570         src : {
25571             title: "Src",
25572             width: 220
25573         }
25574         
25575     },
25576     'A' : {
25577         name : {
25578             title: "Name",
25579             width: 50
25580         },
25581         href:  {
25582             title: "Href",
25583             width: 220
25584         } // border?
25585         
25586     },
25587     'TABLE' : {
25588         rows : {
25589             title: "Rows",
25590             width: 20
25591         },
25592         cols : {
25593             title: "Cols",
25594             width: 20
25595         },
25596         width : {
25597             title: "Width",
25598             width: 40
25599         },
25600         height : {
25601             title: "Height",
25602             width: 40
25603         },
25604         border : {
25605             title: "Border",
25606             width: 20
25607         }
25608     },
25609     'TD' : {
25610         width : {
25611             title: "Width",
25612             width: 40
25613         },
25614         height : {
25615             title: "Height",
25616             width: 40
25617         },   
25618         align: {
25619             title: "Align",
25620             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25621             width: 40
25622         },
25623         valign: {
25624             title: "Valign",
25625             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25626             width: 40
25627         },
25628         colspan: {
25629             title: "Colspan",
25630             width: 20
25631             
25632         }
25633     },
25634     'INPUT' : {
25635         name : {
25636             title: "name",
25637             width: 120
25638         },
25639         value : {
25640             title: "Value",
25641             width: 120
25642         },
25643         width : {
25644             title: "Width",
25645             width: 40
25646         }
25647     },
25648     'LABEL' : {
25649         'for' : {
25650             title: "For",
25651             width: 120
25652         }
25653     },
25654     'TEXTAREA' : {
25655           name : {
25656             title: "name",
25657             width: 120
25658         },
25659         rows : {
25660             title: "Rows",
25661             width: 20
25662         },
25663         cols : {
25664             title: "Cols",
25665             width: 20
25666         }
25667     },
25668     'SELECT' : {
25669         name : {
25670             title: "name",
25671             width: 120
25672         },
25673         selectoptions : {
25674             title: "Options",
25675             width: 200
25676         }
25677     },
25678     'BODY' : {
25679         title : {
25680             title: "title",
25681             width: 120,
25682             disabled : true
25683         }
25684     }
25685 };
25686
25687
25688
25689 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25690     
25691     tb: false,
25692     
25693     rendered: false,
25694     
25695     editor : false,
25696     /**
25697      * @cfg {Object} disable  List of toolbar elements to disable
25698          
25699      */
25700     disable : false,
25701     
25702     
25703     
25704     toolbars : false,
25705     
25706     init : function(editor)
25707     {
25708         this.editor = editor;
25709         
25710         
25711         var fid = editor.frameId;
25712         var etb = this;
25713         function btn(id, toggle, handler){
25714             var xid = fid + '-'+ id ;
25715             return {
25716                 id : xid,
25717                 cmd : id,
25718                 cls : 'x-btn-icon x-edit-'+id,
25719                 enableToggle:toggle !== false,
25720                 scope: editor, // was editor...
25721                 handler:handler||editor.relayBtnCmd,
25722                 clickEvent:'mousedown',
25723                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25724                 tabIndex:-1
25725             };
25726         }
25727         // create a new element.
25728         var wdiv = editor.wrap.createChild({
25729                 tag: 'div'
25730             }, editor.wrap.dom.firstChild.nextSibling, true);
25731         
25732         // can we do this more than once??
25733         
25734          // stop form submits
25735       
25736  
25737         // disable everything...
25738         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25739         this.toolbars = {};
25740            
25741         for (var i in  ty) {
25742           
25743             this.toolbars[i] = this.buildToolbar(ty[i],i);
25744         }
25745         this.tb = this.toolbars.BODY;
25746         this.tb.el.show();
25747         
25748          
25749         this.rendered = true;
25750         
25751         // the all the btns;
25752         editor.on('editorevent', this.updateToolbar, this);
25753         // other toolbars need to implement this..
25754         //editor.on('editmodechange', this.updateToolbar, this);
25755     },
25756     
25757     
25758     
25759     /**
25760      * Protected method that will not generally be called directly. It triggers
25761      * a toolbar update by reading the markup state of the current selection in the editor.
25762      */
25763     updateToolbar: function(){
25764
25765         if(!this.editor.activated){
25766             this.editor.onFirstFocus();
25767             return;
25768         }
25769
25770         
25771         var ans = this.editor.getAllAncestors();
25772         
25773         // pick
25774         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25775         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25776         sel = sel ? sel : this.editor.doc.body;
25777         sel = sel.tagName.length ? sel : this.editor.doc.body;
25778         var tn = sel.tagName.toUpperCase();
25779         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25780         tn = sel.tagName.toUpperCase();
25781         if (this.tb.name  == tn) {
25782             return; // no change
25783         }
25784         this.tb.el.hide();
25785         ///console.log("show: " + tn);
25786         this.tb =  this.toolbars[tn];
25787         this.tb.el.show();
25788         this.tb.fields.each(function(e) {
25789             e.setValue(sel.getAttribute(e.name));
25790         });
25791         this.tb.selectedNode = sel;
25792         
25793         
25794         Roo.menu.MenuMgr.hideAll();
25795
25796         //this.editorsyncValue();
25797     },
25798    
25799        
25800     // private
25801     onDestroy : function(){
25802         if(this.rendered){
25803             
25804             this.tb.items.each(function(item){
25805                 if(item.menu){
25806                     item.menu.removeAll();
25807                     if(item.menu.el){
25808                         item.menu.el.destroy();
25809                     }
25810                 }
25811                 item.destroy();
25812             });
25813              
25814         }
25815     },
25816     onFirstFocus: function() {
25817         // need to do this for all the toolbars..
25818         this.tb.items.each(function(item){
25819            item.enable();
25820         });
25821     },
25822     buildToolbar: function(tlist, nm)
25823     {
25824         var editor = this.editor;
25825          // create a new element.
25826         var wdiv = editor.wrap.createChild({
25827                 tag: 'div'
25828             }, editor.wrap.dom.firstChild.nextSibling, true);
25829         
25830        
25831         var tb = new Roo.Toolbar(wdiv);
25832         tb.add(nm+ ":&nbsp;");
25833         for (var i in tlist) {
25834             var item = tlist[i];
25835             tb.add(item.title + ":&nbsp;");
25836             if (item.opts) {
25837                 // fixme
25838                 
25839               
25840                 tb.addField( new Roo.form.ComboBox({
25841                     store: new Roo.data.SimpleStore({
25842                         id : 'val',
25843                         fields: ['val'],
25844                         data : item.opts // from states.js
25845                     }),
25846                     name : i,
25847                     displayField:'val',
25848                     typeAhead: false,
25849                     mode: 'local',
25850                     editable : false,
25851                     triggerAction: 'all',
25852                     emptyText:'Select',
25853                     selectOnFocus:true,
25854                     width: item.width ? item.width  : 130,
25855                     listeners : {
25856                         'select': function(c, r, i) {
25857                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25858                         }
25859                     }
25860
25861                 }));
25862                 continue;
25863                     
25864                 
25865                 
25866                 
25867                 
25868                 tb.addField( new Roo.form.TextField({
25869                     name: i,
25870                     width: 100,
25871                     //allowBlank:false,
25872                     value: ''
25873                 }));
25874                 continue;
25875             }
25876             tb.addField( new Roo.form.TextField({
25877                 name: i,
25878                 width: item.width,
25879                 //allowBlank:true,
25880                 value: '',
25881                 listeners: {
25882                     'change' : function(f, nv, ov) {
25883                         tb.selectedNode.setAttribute(f.name, nv);
25884                     }
25885                 }
25886             }));
25887              
25888         }
25889         tb.el.on('click', function(e){
25890             e.preventDefault(); // what does this do?
25891         });
25892         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25893         tb.el.hide();
25894         tb.name = nm;
25895         // dont need to disable them... as they will get hidden
25896         return tb;
25897          
25898         
25899     }
25900     
25901     
25902     
25903     
25904 });
25905
25906
25907
25908
25909
25910 /*
25911  * Based on:
25912  * Ext JS Library 1.1.1
25913  * Copyright(c) 2006-2007, Ext JS, LLC.
25914  *
25915  * Originally Released Under LGPL - original licence link has changed is not relivant.
25916  *
25917  * Fork - LGPL
25918  * <script type="text/javascript">
25919  */
25920  
25921 /**
25922  * @class Roo.form.BasicForm
25923  * @extends Roo.util.Observable
25924  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25925  * @constructor
25926  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25927  * @param {Object} config Configuration options
25928  */
25929 Roo.form.BasicForm = function(el, config){
25930     this.allItems = [];
25931     this.childForms = [];
25932     Roo.apply(this, config);
25933     /*
25934      * The Roo.form.Field items in this form.
25935      * @type MixedCollection
25936      */
25937      
25938      
25939     this.items = new Roo.util.MixedCollection(false, function(o){
25940         return o.id || (o.id = Roo.id());
25941     });
25942     this.addEvents({
25943         /**
25944          * @event beforeaction
25945          * Fires before any action is performed. Return false to cancel the action.
25946          * @param {Form} this
25947          * @param {Action} action The action to be performed
25948          */
25949         beforeaction: true,
25950         /**
25951          * @event actionfailed
25952          * Fires when an action fails.
25953          * @param {Form} this
25954          * @param {Action} action The action that failed
25955          */
25956         actionfailed : true,
25957         /**
25958          * @event actioncomplete
25959          * Fires when an action is completed.
25960          * @param {Form} this
25961          * @param {Action} action The action that completed
25962          */
25963         actioncomplete : true
25964     });
25965     if(el){
25966         this.initEl(el);
25967     }
25968     Roo.form.BasicForm.superclass.constructor.call(this);
25969 };
25970
25971 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25972     /**
25973      * @cfg {String} method
25974      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25975      */
25976     /**
25977      * @cfg {DataReader} reader
25978      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25979      * This is optional as there is built-in support for processing JSON.
25980      */
25981     /**
25982      * @cfg {DataReader} errorReader
25983      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25984      * This is completely optional as there is built-in support for processing JSON.
25985      */
25986     /**
25987      * @cfg {String} url
25988      * The URL to use for form actions if one isn't supplied in the action options.
25989      */
25990     /**
25991      * @cfg {Boolean} fileUpload
25992      * Set to true if this form is a file upload.
25993      */
25994      
25995     /**
25996      * @cfg {Object} baseParams
25997      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25998      */
25999      /**
26000      
26001     /**
26002      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26003      */
26004     timeout: 30,
26005
26006     // private
26007     activeAction : null,
26008
26009     /**
26010      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26011      * or setValues() data instead of when the form was first created.
26012      */
26013     trackResetOnLoad : false,
26014     
26015     
26016     /**
26017      * childForms - used for multi-tab forms
26018      * @type {Array}
26019      */
26020     childForms : false,
26021     
26022     /**
26023      * allItems - full list of fields.
26024      * @type {Array}
26025      */
26026     allItems : false,
26027     
26028     /**
26029      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26030      * element by passing it or its id or mask the form itself by passing in true.
26031      * @type Mixed
26032      */
26033     waitMsgTarget : false,
26034
26035     // private
26036     initEl : function(el){
26037         this.el = Roo.get(el);
26038         this.id = this.el.id || Roo.id();
26039         this.el.on('submit', this.onSubmit, this);
26040         this.el.addClass('x-form');
26041     },
26042
26043     // private
26044     onSubmit : function(e){
26045         e.stopEvent();
26046     },
26047
26048     /**
26049      * Returns true if client-side validation on the form is successful.
26050      * @return Boolean
26051      */
26052     isValid : function(){
26053         var valid = true;
26054         this.items.each(function(f){
26055            if(!f.validate()){
26056                valid = false;
26057            }
26058         });
26059         return valid;
26060     },
26061
26062     /**
26063      * Returns true if any fields in this form have changed since their original load.
26064      * @return Boolean
26065      */
26066     isDirty : function(){
26067         var dirty = false;
26068         this.items.each(function(f){
26069            if(f.isDirty()){
26070                dirty = true;
26071                return false;
26072            }
26073         });
26074         return dirty;
26075     },
26076
26077     /**
26078      * Performs a predefined action (submit or load) or custom actions you define on this form.
26079      * @param {String} actionName The name of the action type
26080      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26081      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26082      * accept other config options):
26083      * <pre>
26084 Property          Type             Description
26085 ----------------  ---------------  ----------------------------------------------------------------------------------
26086 url               String           The url for the action (defaults to the form's url)
26087 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26088 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26089 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26090                                    validate the form on the client (defaults to false)
26091      * </pre>
26092      * @return {BasicForm} this
26093      */
26094     doAction : function(action, options){
26095         if(typeof action == 'string'){
26096             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26097         }
26098         if(this.fireEvent('beforeaction', this, action) !== false){
26099             this.beforeAction(action);
26100             action.run.defer(100, action);
26101         }
26102         return this;
26103     },
26104
26105     /**
26106      * Shortcut to do a submit action.
26107      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26108      * @return {BasicForm} this
26109      */
26110     submit : function(options){
26111         this.doAction('submit', options);
26112         return this;
26113     },
26114
26115     /**
26116      * Shortcut to do a load action.
26117      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26118      * @return {BasicForm} this
26119      */
26120     load : function(options){
26121         this.doAction('load', options);
26122         return this;
26123     },
26124
26125     /**
26126      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26127      * @param {Record} record The record to edit
26128      * @return {BasicForm} this
26129      */
26130     updateRecord : function(record){
26131         record.beginEdit();
26132         var fs = record.fields;
26133         fs.each(function(f){
26134             var field = this.findField(f.name);
26135             if(field){
26136                 record.set(f.name, field.getValue());
26137             }
26138         }, this);
26139         record.endEdit();
26140         return this;
26141     },
26142
26143     /**
26144      * Loads an Roo.data.Record into this form.
26145      * @param {Record} record The record to load
26146      * @return {BasicForm} this
26147      */
26148     loadRecord : function(record){
26149         this.setValues(record.data);
26150         return this;
26151     },
26152
26153     // private
26154     beforeAction : function(action){
26155         var o = action.options;
26156         
26157        
26158         if(this.waitMsgTarget === true){
26159             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26160         }else if(this.waitMsgTarget){
26161             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26162             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26163         }else {
26164             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26165         }
26166          
26167     },
26168
26169     // private
26170     afterAction : function(action, success){
26171         this.activeAction = null;
26172         var o = action.options;
26173         
26174         if(this.waitMsgTarget === true){
26175             this.el.unmask();
26176         }else if(this.waitMsgTarget){
26177             this.waitMsgTarget.unmask();
26178         }else{
26179             Roo.MessageBox.updateProgress(1);
26180             Roo.MessageBox.hide();
26181         }
26182          
26183         if(success){
26184             if(o.reset){
26185                 this.reset();
26186             }
26187             Roo.callback(o.success, o.scope, [this, action]);
26188             this.fireEvent('actioncomplete', this, action);
26189             
26190         }else{
26191             Roo.callback(o.failure, o.scope, [this, action]);
26192             // show an error message if no failed handler is set..
26193             if (!this.hasListener('actionfailed')) {
26194                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26195             }
26196             
26197             this.fireEvent('actionfailed', this, action);
26198         }
26199         
26200     },
26201
26202     /**
26203      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26204      * @param {String} id The value to search for
26205      * @return Field
26206      */
26207     findField : function(id){
26208         var field = this.items.get(id);
26209         if(!field){
26210             this.items.each(function(f){
26211                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26212                     field = f;
26213                     return false;
26214                 }
26215             });
26216         }
26217         return field || null;
26218     },
26219
26220     /**
26221      * Add a secondary form to this one, 
26222      * Used to provide tabbed forms. One form is primary, with hidden values 
26223      * which mirror the elements from the other forms.
26224      * 
26225      * @param {Roo.form.Form} form to add.
26226      * 
26227      */
26228     addForm : function(form)
26229     {
26230        
26231         if (this.childForms.indexOf(form) > -1) {
26232             // already added..
26233             return;
26234         }
26235         this.childForms.push(form);
26236         var n = '';
26237         Roo.each(form.allItems, function (fe) {
26238             
26239             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26240             if (this.findField(n)) { // already added..
26241                 return;
26242             }
26243             var add = new Roo.form.Hidden({
26244                 name : n
26245             });
26246             add.render(this.el);
26247             
26248             this.add( add );
26249         }, this);
26250         
26251     },
26252     /**
26253      * Mark fields in this form invalid in bulk.
26254      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26255      * @return {BasicForm} this
26256      */
26257     markInvalid : function(errors){
26258         if(errors instanceof Array){
26259             for(var i = 0, len = errors.length; i < len; i++){
26260                 var fieldError = errors[i];
26261                 var f = this.findField(fieldError.id);
26262                 if(f){
26263                     f.markInvalid(fieldError.msg);
26264                 }
26265             }
26266         }else{
26267             var field, id;
26268             for(id in errors){
26269                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26270                     field.markInvalid(errors[id]);
26271                 }
26272             }
26273         }
26274         Roo.each(this.childForms || [], function (f) {
26275             f.markInvalid(errors);
26276         });
26277         
26278         return this;
26279     },
26280
26281     /**
26282      * Set values for fields in this form in bulk.
26283      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26284      * @return {BasicForm} this
26285      */
26286     setValues : function(values){
26287         if(values instanceof Array){ // array of objects
26288             for(var i = 0, len = values.length; i < len; i++){
26289                 var v = values[i];
26290                 var f = this.findField(v.id);
26291                 if(f){
26292                     f.setValue(v.value);
26293                     if(this.trackResetOnLoad){
26294                         f.originalValue = f.getValue();
26295                     }
26296                 }
26297             }
26298         }else{ // object hash
26299             var field, id;
26300             for(id in values){
26301                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26302                     
26303                     if (field.setFromData && 
26304                         field.valueField && 
26305                         field.displayField &&
26306                         // combos' with local stores can 
26307                         // be queried via setValue()
26308                         // to set their value..
26309                         (field.store && !field.store.isLocal)
26310                         ) {
26311                         // it's a combo
26312                         var sd = { };
26313                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26314                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26315                         field.setFromData(sd);
26316                         
26317                     } else {
26318                         field.setValue(values[id]);
26319                     }
26320                     
26321                     
26322                     if(this.trackResetOnLoad){
26323                         field.originalValue = field.getValue();
26324                     }
26325                 }
26326             }
26327         }
26328          
26329         Roo.each(this.childForms || [], function (f) {
26330             f.setValues(values);
26331         });
26332                 
26333         return this;
26334     },
26335
26336     /**
26337      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26338      * they are returned as an array.
26339      * @param {Boolean} asString
26340      * @return {Object}
26341      */
26342     getValues : function(asString){
26343         if (this.childForms) {
26344             // copy values from the child forms
26345             Roo.each(this.childForms, function (f) {
26346                 this.setValues(f.getValues());
26347             }, this);
26348         }
26349         
26350         
26351         
26352         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26353         if(asString === true){
26354             return fs;
26355         }
26356         return Roo.urlDecode(fs);
26357     },
26358     
26359     /**
26360      * Returns the fields in this form as an object with key/value pairs. 
26361      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26362      * @return {Object}
26363      */
26364     getFieldValues : function()
26365     {
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         var ret = {};
26374         this.items.each(function(f){
26375             if (!f.getName()) {
26376                 return;
26377             }
26378             var v = f.getValue();
26379             if ((typeof(v) == 'object') && f.getRawValue) {
26380                 v = f.getRawValue() ; // dates..
26381             }
26382             ret[f.getName()] = v;
26383         });
26384         
26385         return ret;
26386     },
26387
26388     /**
26389      * Clears all invalid messages in this form.
26390      * @return {BasicForm} this
26391      */
26392     clearInvalid : function(){
26393         this.items.each(function(f){
26394            f.clearInvalid();
26395         });
26396         
26397         Roo.each(this.childForms || [], function (f) {
26398             f.clearInvalid();
26399         });
26400         
26401         
26402         return this;
26403     },
26404
26405     /**
26406      * Resets this form.
26407      * @return {BasicForm} this
26408      */
26409     reset : function(){
26410         this.items.each(function(f){
26411             f.reset();
26412         });
26413         
26414         Roo.each(this.childForms || [], function (f) {
26415             f.reset();
26416         });
26417        
26418         
26419         return this;
26420     },
26421
26422     /**
26423      * Add Roo.form components to this form.
26424      * @param {Field} field1
26425      * @param {Field} field2 (optional)
26426      * @param {Field} etc (optional)
26427      * @return {BasicForm} this
26428      */
26429     add : function(){
26430         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26431         return this;
26432     },
26433
26434
26435     /**
26436      * Removes a field from the items collection (does NOT remove its markup).
26437      * @param {Field} field
26438      * @return {BasicForm} this
26439      */
26440     remove : function(field){
26441         this.items.remove(field);
26442         return this;
26443     },
26444
26445     /**
26446      * Looks at the fields in this form, checks them for an id attribute,
26447      * and calls applyTo on the existing dom element with that id.
26448      * @return {BasicForm} this
26449      */
26450     render : function(){
26451         this.items.each(function(f){
26452             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26453                 f.applyTo(f.id);
26454             }
26455         });
26456         return this;
26457     },
26458
26459     /**
26460      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26461      * @param {Object} values
26462      * @return {BasicForm} this
26463      */
26464     applyToFields : function(o){
26465         this.items.each(function(f){
26466            Roo.apply(f, o);
26467         });
26468         return this;
26469     },
26470
26471     /**
26472      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26473      * @param {Object} values
26474      * @return {BasicForm} this
26475      */
26476     applyIfToFields : function(o){
26477         this.items.each(function(f){
26478            Roo.applyIf(f, o);
26479         });
26480         return this;
26481     }
26482 });
26483
26484 // back compat
26485 Roo.BasicForm = Roo.form.BasicForm;/*
26486  * Based on:
26487  * Ext JS Library 1.1.1
26488  * Copyright(c) 2006-2007, Ext JS, LLC.
26489  *
26490  * Originally Released Under LGPL - original licence link has changed is not relivant.
26491  *
26492  * Fork - LGPL
26493  * <script type="text/javascript">
26494  */
26495
26496 /**
26497  * @class Roo.form.Form
26498  * @extends Roo.form.BasicForm
26499  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26500  * @constructor
26501  * @param {Object} config Configuration options
26502  */
26503 Roo.form.Form = function(config){
26504     var xitems =  [];
26505     if (config.items) {
26506         xitems = config.items;
26507         delete config.items;
26508     }
26509    
26510     
26511     Roo.form.Form.superclass.constructor.call(this, null, config);
26512     this.url = this.url || this.action;
26513     if(!this.root){
26514         this.root = new Roo.form.Layout(Roo.applyIf({
26515             id: Roo.id()
26516         }, config));
26517     }
26518     this.active = this.root;
26519     /**
26520      * Array of all the buttons that have been added to this form via {@link addButton}
26521      * @type Array
26522      */
26523     this.buttons = [];
26524     this.allItems = [];
26525     this.addEvents({
26526         /**
26527          * @event clientvalidation
26528          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26529          * @param {Form} this
26530          * @param {Boolean} valid true if the form has passed client-side validation
26531          */
26532         clientvalidation: true,
26533         /**
26534          * @event rendered
26535          * Fires when the form is rendered
26536          * @param {Roo.form.Form} form
26537          */
26538         rendered : true
26539     });
26540     
26541     if (this.progressUrl) {
26542             // push a hidden field onto the list of fields..
26543             this.addxtype( {
26544                     xns: Roo.form, 
26545                     xtype : 'Hidden', 
26546                     name : 'UPLOAD_IDENTIFIER' 
26547             });
26548         }
26549         
26550     
26551     Roo.each(xitems, this.addxtype, this);
26552     
26553     
26554     
26555 };
26556
26557 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26558     /**
26559      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26560      */
26561     /**
26562      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26563      */
26564     /**
26565      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26566      */
26567     buttonAlign:'center',
26568
26569     /**
26570      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26571      */
26572     minButtonWidth:75,
26573
26574     /**
26575      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26576      * This property cascades to child containers if not set.
26577      */
26578     labelAlign:'left',
26579
26580     /**
26581      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26582      * fires a looping event with that state. This is required to bind buttons to the valid
26583      * state using the config value formBind:true on the button.
26584      */
26585     monitorValid : false,
26586
26587     /**
26588      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26589      */
26590     monitorPoll : 200,
26591     
26592     /**
26593      * @cfg {String} progressUrl - Url to return progress data 
26594      */
26595     
26596     progressUrl : false,
26597   
26598     /**
26599      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26600      * fields are added and the column is closed. If no fields are passed the column remains open
26601      * until end() is called.
26602      * @param {Object} config The config to pass to the column
26603      * @param {Field} field1 (optional)
26604      * @param {Field} field2 (optional)
26605      * @param {Field} etc (optional)
26606      * @return Column The column container object
26607      */
26608     column : function(c){
26609         var col = new Roo.form.Column(c);
26610         this.start(col);
26611         if(arguments.length > 1){ // duplicate code required because of Opera
26612             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26613             this.end();
26614         }
26615         return col;
26616     },
26617
26618     /**
26619      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26620      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26621      * until end() is called.
26622      * @param {Object} config The config to pass to the fieldset
26623      * @param {Field} field1 (optional)
26624      * @param {Field} field2 (optional)
26625      * @param {Field} etc (optional)
26626      * @return FieldSet The fieldset container object
26627      */
26628     fieldset : function(c){
26629         var fs = new Roo.form.FieldSet(c);
26630         this.start(fs);
26631         if(arguments.length > 1){ // duplicate code required because of Opera
26632             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26633             this.end();
26634         }
26635         return fs;
26636     },
26637
26638     /**
26639      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26640      * fields are added and the container is closed. If no fields are passed the container remains open
26641      * until end() is called.
26642      * @param {Object} config The config to pass to the Layout
26643      * @param {Field} field1 (optional)
26644      * @param {Field} field2 (optional)
26645      * @param {Field} etc (optional)
26646      * @return Layout The container object
26647      */
26648     container : function(c){
26649         var l = new Roo.form.Layout(c);
26650         this.start(l);
26651         if(arguments.length > 1){ // duplicate code required because of Opera
26652             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26653             this.end();
26654         }
26655         return l;
26656     },
26657
26658     /**
26659      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26660      * @param {Object} container A Roo.form.Layout or subclass of Layout
26661      * @return {Form} this
26662      */
26663     start : function(c){
26664         // cascade label info
26665         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26666         this.active.stack.push(c);
26667         c.ownerCt = this.active;
26668         this.active = c;
26669         return this;
26670     },
26671
26672     /**
26673      * Closes the current open container
26674      * @return {Form} this
26675      */
26676     end : function(){
26677         if(this.active == this.root){
26678             return this;
26679         }
26680         this.active = this.active.ownerCt;
26681         return this;
26682     },
26683
26684     /**
26685      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26686      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26687      * as the label of the field.
26688      * @param {Field} field1
26689      * @param {Field} field2 (optional)
26690      * @param {Field} etc. (optional)
26691      * @return {Form} this
26692      */
26693     add : function(){
26694         this.active.stack.push.apply(this.active.stack, arguments);
26695         this.allItems.push.apply(this.allItems,arguments);
26696         var r = [];
26697         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26698             if(a[i].isFormField){
26699                 r.push(a[i]);
26700             }
26701         }
26702         if(r.length > 0){
26703             Roo.form.Form.superclass.add.apply(this, r);
26704         }
26705         return this;
26706     },
26707     
26708
26709     
26710     
26711     
26712      /**
26713      * Find any element that has been added to a form, using it's ID or name
26714      * This can include framesets, columns etc. along with regular fields..
26715      * @param {String} id - id or name to find.
26716      
26717      * @return {Element} e - or false if nothing found.
26718      */
26719     findbyId : function(id)
26720     {
26721         var ret = false;
26722         if (!id) {
26723             return ret;
26724         }
26725         Ext.each(this.allItems, function(f){
26726             if (f.id == id || f.name == id ){
26727                 ret = f;
26728                 return false;
26729             }
26730         });
26731         return ret;
26732     },
26733
26734     
26735     
26736     /**
26737      * Render this form into the passed container. This should only be called once!
26738      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26739      * @return {Form} this
26740      */
26741     render : function(ct)
26742     {
26743         
26744         
26745         
26746         ct = Roo.get(ct);
26747         var o = this.autoCreate || {
26748             tag: 'form',
26749             method : this.method || 'POST',
26750             id : this.id || Roo.id()
26751         };
26752         this.initEl(ct.createChild(o));
26753
26754         this.root.render(this.el);
26755         
26756        
26757              
26758         this.items.each(function(f){
26759             f.render('x-form-el-'+f.id);
26760         });
26761
26762         if(this.buttons.length > 0){
26763             // tables are required to maintain order and for correct IE layout
26764             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26765                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26766                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26767             }}, null, true);
26768             var tr = tb.getElementsByTagName('tr')[0];
26769             for(var i = 0, len = this.buttons.length; i < len; i++) {
26770                 var b = this.buttons[i];
26771                 var td = document.createElement('td');
26772                 td.className = 'x-form-btn-td';
26773                 b.render(tr.appendChild(td));
26774             }
26775         }
26776         if(this.monitorValid){ // initialize after render
26777             this.startMonitoring();
26778         }
26779         this.fireEvent('rendered', this);
26780         return this;
26781     },
26782
26783     /**
26784      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26785      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26786      * object or a valid Roo.DomHelper element config
26787      * @param {Function} handler The function called when the button is clicked
26788      * @param {Object} scope (optional) The scope of the handler function
26789      * @return {Roo.Button}
26790      */
26791     addButton : function(config, handler, scope){
26792         var bc = {
26793             handler: handler,
26794             scope: scope,
26795             minWidth: this.minButtonWidth,
26796             hideParent:true
26797         };
26798         if(typeof config == "string"){
26799             bc.text = config;
26800         }else{
26801             Roo.apply(bc, config);
26802         }
26803         var btn = new Roo.Button(null, bc);
26804         this.buttons.push(btn);
26805         return btn;
26806     },
26807
26808      /**
26809      * Adds a series of form elements (using the xtype property as the factory method.
26810      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26811      * @param {Object} config 
26812      */
26813     
26814     addxtype : function()
26815     {
26816         var ar = Array.prototype.slice.call(arguments, 0);
26817         var ret = false;
26818         for(var i = 0; i < ar.length; i++) {
26819             if (!ar[i]) {
26820                 continue; // skip -- if this happends something invalid got sent, we 
26821                 // should ignore it, as basically that interface element will not show up
26822                 // and that should be pretty obvious!!
26823             }
26824             
26825             if (Roo.form[ar[i].xtype]) {
26826                 ar[i].form = this;
26827                 var fe = Roo.factory(ar[i], Roo.form);
26828                 if (!ret) {
26829                     ret = fe;
26830                 }
26831                 fe.form = this;
26832                 if (fe.store) {
26833                     fe.store.form = this;
26834                 }
26835                 if (fe.isLayout) {  
26836                          
26837                     this.start(fe);
26838                     this.allItems.push(fe);
26839                     if (fe.items && fe.addxtype) {
26840                         fe.addxtype.apply(fe, fe.items);
26841                         delete fe.items;
26842                     }
26843                      this.end();
26844                     continue;
26845                 }
26846                 
26847                 
26848                  
26849                 this.add(fe);
26850               //  console.log('adding ' + ar[i].xtype);
26851             }
26852             if (ar[i].xtype == 'Button') {  
26853                 //console.log('adding button');
26854                 //console.log(ar[i]);
26855                 this.addButton(ar[i]);
26856                 this.allItems.push(fe);
26857                 continue;
26858             }
26859             
26860             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26861                 alert('end is not supported on xtype any more, use items');
26862             //    this.end();
26863             //    //console.log('adding end');
26864             }
26865             
26866         }
26867         return ret;
26868     },
26869     
26870     /**
26871      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26872      * option "monitorValid"
26873      */
26874     startMonitoring : function(){
26875         if(!this.bound){
26876             this.bound = true;
26877             Roo.TaskMgr.start({
26878                 run : this.bindHandler,
26879                 interval : this.monitorPoll || 200,
26880                 scope: this
26881             });
26882         }
26883     },
26884
26885     /**
26886      * Stops monitoring of the valid state of this form
26887      */
26888     stopMonitoring : function(){
26889         this.bound = false;
26890     },
26891
26892     // private
26893     bindHandler : function(){
26894         if(!this.bound){
26895             return false; // stops binding
26896         }
26897         var valid = true;
26898         this.items.each(function(f){
26899             if(!f.isValid(true)){
26900                 valid = false;
26901                 return false;
26902             }
26903         });
26904         for(var i = 0, len = this.buttons.length; i < len; i++){
26905             var btn = this.buttons[i];
26906             if(btn.formBind === true && btn.disabled === valid){
26907                 btn.setDisabled(!valid);
26908             }
26909         }
26910         this.fireEvent('clientvalidation', this, valid);
26911     }
26912     
26913     
26914     
26915     
26916     
26917     
26918     
26919     
26920 });
26921
26922
26923 // back compat
26924 Roo.Form = Roo.form.Form;
26925 /*
26926  * Based on:
26927  * Ext JS Library 1.1.1
26928  * Copyright(c) 2006-2007, Ext JS, LLC.
26929  *
26930  * Originally Released Under LGPL - original licence link has changed is not relivant.
26931  *
26932  * Fork - LGPL
26933  * <script type="text/javascript">
26934  */
26935  
26936  /**
26937  * @class Roo.form.Action
26938  * Internal Class used to handle form actions
26939  * @constructor
26940  * @param {Roo.form.BasicForm} el The form element or its id
26941  * @param {Object} config Configuration options
26942  */
26943  
26944  
26945 // define the action interface
26946 Roo.form.Action = function(form, options){
26947     this.form = form;
26948     this.options = options || {};
26949 };
26950 /**
26951  * Client Validation Failed
26952  * @const 
26953  */
26954 Roo.form.Action.CLIENT_INVALID = 'client';
26955 /**
26956  * Server Validation Failed
26957  * @const 
26958  */
26959  Roo.form.Action.SERVER_INVALID = 'server';
26960  /**
26961  * Connect to Server Failed
26962  * @const 
26963  */
26964 Roo.form.Action.CONNECT_FAILURE = 'connect';
26965 /**
26966  * Reading Data from Server Failed
26967  * @const 
26968  */
26969 Roo.form.Action.LOAD_FAILURE = 'load';
26970
26971 Roo.form.Action.prototype = {
26972     type : 'default',
26973     failureType : undefined,
26974     response : undefined,
26975     result : undefined,
26976
26977     // interface method
26978     run : function(options){
26979
26980     },
26981
26982     // interface method
26983     success : function(response){
26984
26985     },
26986
26987     // interface method
26988     handleResponse : function(response){
26989
26990     },
26991
26992     // default connection failure
26993     failure : function(response){
26994         
26995         this.response = response;
26996         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26997         this.form.afterAction(this, false);
26998     },
26999
27000     processResponse : function(response){
27001         this.response = response;
27002         if(!response.responseText){
27003             return true;
27004         }
27005         this.result = this.handleResponse(response);
27006         return this.result;
27007     },
27008
27009     // utility functions used internally
27010     getUrl : function(appendParams){
27011         var url = this.options.url || this.form.url || this.form.el.dom.action;
27012         if(appendParams){
27013             var p = this.getParams();
27014             if(p){
27015                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27016             }
27017         }
27018         return url;
27019     },
27020
27021     getMethod : function(){
27022         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27023     },
27024
27025     getParams : function(){
27026         var bp = this.form.baseParams;
27027         var p = this.options.params;
27028         if(p){
27029             if(typeof p == "object"){
27030                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27031             }else if(typeof p == 'string' && bp){
27032                 p += '&' + Roo.urlEncode(bp);
27033             }
27034         }else if(bp){
27035             p = Roo.urlEncode(bp);
27036         }
27037         return p;
27038     },
27039
27040     createCallback : function(){
27041         return {
27042             success: this.success,
27043             failure: this.failure,
27044             scope: this,
27045             timeout: (this.form.timeout*1000),
27046             upload: this.form.fileUpload ? this.success : undefined
27047         };
27048     }
27049 };
27050
27051 Roo.form.Action.Submit = function(form, options){
27052     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27053 };
27054
27055 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27056     type : 'submit',
27057
27058     haveProgress : false,
27059     uploadComplete : false,
27060     
27061     // uploadProgress indicator.
27062     uploadProgress : function()
27063     {
27064         if (!this.form.progressUrl) {
27065             return;
27066         }
27067         
27068         if (!this.haveProgress) {
27069             Roo.MessageBox.progress("Uploading", "Uploading");
27070         }
27071         if (this.uploadComplete) {
27072            Roo.MessageBox.hide();
27073            return;
27074         }
27075         
27076         this.haveProgress = true;
27077    
27078         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27079         
27080         var c = new Roo.data.Connection();
27081         c.request({
27082             url : this.form.progressUrl,
27083             params: {
27084                 id : uid
27085             },
27086             method: 'GET',
27087             success : function(req){
27088                //console.log(data);
27089                 var rdata = false;
27090                 var edata;
27091                 try  {
27092                    rdata = Roo.decode(req.responseText)
27093                 } catch (e) {
27094                     Roo.log("Invalid data from server..");
27095                     Roo.log(edata);
27096                     return;
27097                 }
27098                 if (!rdata || !rdata.success) {
27099                     Roo.log(rdata);
27100                     return;
27101                 }
27102                 var data = rdata.data;
27103                 
27104                 if (this.uploadComplete) {
27105                    Roo.MessageBox.hide();
27106                    return;
27107                 }
27108                    
27109                 if (data){
27110                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27111                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27112                     );
27113                 }
27114                 this.uploadProgress.defer(2000,this);
27115             },
27116        
27117             failure: function(data) {
27118                 Roo.log('progress url failed ');
27119                 Roo.log(data);
27120             },
27121             scope : this
27122         });
27123            
27124     },
27125     
27126     
27127     run : function()
27128     {
27129         // run get Values on the form, so it syncs any secondary forms.
27130         this.form.getValues();
27131         
27132         var o = this.options;
27133         var method = this.getMethod();
27134         var isPost = method == 'POST';
27135         if(o.clientValidation === false || this.form.isValid()){
27136             
27137             if (this.form.progressUrl) {
27138                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27139                     (new Date() * 1) + '' + Math.random());
27140                     
27141             } 
27142             
27143             
27144             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27145                 form:this.form.el.dom,
27146                 url:this.getUrl(!isPost),
27147                 method: method,
27148                 params:isPost ? this.getParams() : null,
27149                 isUpload: this.form.fileUpload
27150             }));
27151             
27152             this.uploadProgress();
27153
27154         }else if (o.clientValidation !== false){ // client validation failed
27155             this.failureType = Roo.form.Action.CLIENT_INVALID;
27156             this.form.afterAction(this, false);
27157         }
27158     },
27159
27160     success : function(response)
27161     {
27162         this.uploadComplete= true;
27163         if (this.haveProgress) {
27164             Roo.MessageBox.hide();
27165         }
27166         
27167         
27168         var result = this.processResponse(response);
27169         if(result === true || result.success){
27170             this.form.afterAction(this, true);
27171             return;
27172         }
27173         if(result.errors){
27174             this.form.markInvalid(result.errors);
27175             this.failureType = Roo.form.Action.SERVER_INVALID;
27176         }
27177         this.form.afterAction(this, false);
27178     },
27179     failure : function(response)
27180     {
27181         this.uploadComplete= true;
27182         if (this.haveProgress) {
27183             Roo.MessageBox.hide();
27184         }
27185         
27186         
27187         this.response = response;
27188         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27189         this.form.afterAction(this, false);
27190     },
27191     
27192     handleResponse : function(response){
27193         if(this.form.errorReader){
27194             var rs = this.form.errorReader.read(response);
27195             var errors = [];
27196             if(rs.records){
27197                 for(var i = 0, len = rs.records.length; i < len; i++) {
27198                     var r = rs.records[i];
27199                     errors[i] = r.data;
27200                 }
27201             }
27202             if(errors.length < 1){
27203                 errors = null;
27204             }
27205             return {
27206                 success : rs.success,
27207                 errors : errors
27208             };
27209         }
27210         var ret = false;
27211         try {
27212             ret = Roo.decode(response.responseText);
27213         } catch (e) {
27214             ret = {
27215                 success: false,
27216                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27217                 errors : []
27218             };
27219         }
27220         return ret;
27221         
27222     }
27223 });
27224
27225
27226 Roo.form.Action.Load = function(form, options){
27227     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27228     this.reader = this.form.reader;
27229 };
27230
27231 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27232     type : 'load',
27233
27234     run : function(){
27235         
27236         Roo.Ajax.request(Roo.apply(
27237                 this.createCallback(), {
27238                     method:this.getMethod(),
27239                     url:this.getUrl(false),
27240                     params:this.getParams()
27241         }));
27242     },
27243
27244     success : function(response){
27245         
27246         var result = this.processResponse(response);
27247         if(result === true || !result.success || !result.data){
27248             this.failureType = Roo.form.Action.LOAD_FAILURE;
27249             this.form.afterAction(this, false);
27250             return;
27251         }
27252         this.form.clearInvalid();
27253         this.form.setValues(result.data);
27254         this.form.afterAction(this, true);
27255     },
27256
27257     handleResponse : function(response){
27258         if(this.form.reader){
27259             var rs = this.form.reader.read(response);
27260             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27261             return {
27262                 success : rs.success,
27263                 data : data
27264             };
27265         }
27266         return Roo.decode(response.responseText);
27267     }
27268 });
27269
27270 Roo.form.Action.ACTION_TYPES = {
27271     'load' : Roo.form.Action.Load,
27272     'submit' : Roo.form.Action.Submit
27273 };/*
27274  * Based on:
27275  * Ext JS Library 1.1.1
27276  * Copyright(c) 2006-2007, Ext JS, LLC.
27277  *
27278  * Originally Released Under LGPL - original licence link has changed is not relivant.
27279  *
27280  * Fork - LGPL
27281  * <script type="text/javascript">
27282  */
27283  
27284 /**
27285  * @class Roo.form.Layout
27286  * @extends Roo.Component
27287  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27288  * @constructor
27289  * @param {Object} config Configuration options
27290  */
27291 Roo.form.Layout = function(config){
27292     var xitems = [];
27293     if (config.items) {
27294         xitems = config.items;
27295         delete config.items;
27296     }
27297     Roo.form.Layout.superclass.constructor.call(this, config);
27298     this.stack = [];
27299     Roo.each(xitems, this.addxtype, this);
27300      
27301 };
27302
27303 Roo.extend(Roo.form.Layout, Roo.Component, {
27304     /**
27305      * @cfg {String/Object} autoCreate
27306      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27307      */
27308     /**
27309      * @cfg {String/Object/Function} style
27310      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27311      * a function which returns such a specification.
27312      */
27313     /**
27314      * @cfg {String} labelAlign
27315      * Valid values are "left," "top" and "right" (defaults to "left")
27316      */
27317     /**
27318      * @cfg {Number} labelWidth
27319      * Fixed width in pixels of all field labels (defaults to undefined)
27320      */
27321     /**
27322      * @cfg {Boolean} clear
27323      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27324      */
27325     clear : true,
27326     /**
27327      * @cfg {String} labelSeparator
27328      * The separator to use after field labels (defaults to ':')
27329      */
27330     labelSeparator : ':',
27331     /**
27332      * @cfg {Boolean} hideLabels
27333      * True to suppress the display of field labels in this layout (defaults to false)
27334      */
27335     hideLabels : false,
27336
27337     // private
27338     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27339     
27340     isLayout : true,
27341     
27342     // private
27343     onRender : function(ct, position){
27344         if(this.el){ // from markup
27345             this.el = Roo.get(this.el);
27346         }else {  // generate
27347             var cfg = this.getAutoCreate();
27348             this.el = ct.createChild(cfg, position);
27349         }
27350         if(this.style){
27351             this.el.applyStyles(this.style);
27352         }
27353         if(this.labelAlign){
27354             this.el.addClass('x-form-label-'+this.labelAlign);
27355         }
27356         if(this.hideLabels){
27357             this.labelStyle = "display:none";
27358             this.elementStyle = "padding-left:0;";
27359         }else{
27360             if(typeof this.labelWidth == 'number'){
27361                 this.labelStyle = "width:"+this.labelWidth+"px;";
27362                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27363             }
27364             if(this.labelAlign == 'top'){
27365                 this.labelStyle = "width:auto;";
27366                 this.elementStyle = "padding-left:0;";
27367             }
27368         }
27369         var stack = this.stack;
27370         var slen = stack.length;
27371         if(slen > 0){
27372             if(!this.fieldTpl){
27373                 var t = new Roo.Template(
27374                     '<div class="x-form-item {5}">',
27375                         '<label for="{0}" style="{2}">{1}{4}</label>',
27376                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27377                         '</div>',
27378                     '</div><div class="x-form-clear-left"></div>'
27379                 );
27380                 t.disableFormats = true;
27381                 t.compile();
27382                 Roo.form.Layout.prototype.fieldTpl = t;
27383             }
27384             for(var i = 0; i < slen; i++) {
27385                 if(stack[i].isFormField){
27386                     this.renderField(stack[i]);
27387                 }else{
27388                     this.renderComponent(stack[i]);
27389                 }
27390             }
27391         }
27392         if(this.clear){
27393             this.el.createChild({cls:'x-form-clear'});
27394         }
27395     },
27396
27397     // private
27398     renderField : function(f){
27399         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27400                f.id, //0
27401                f.fieldLabel, //1
27402                f.labelStyle||this.labelStyle||'', //2
27403                this.elementStyle||'', //3
27404                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27405                f.itemCls||this.itemCls||''  //5
27406        ], true).getPrevSibling());
27407     },
27408
27409     // private
27410     renderComponent : function(c){
27411         c.render(c.isLayout ? this.el : this.el.createChild());    
27412     },
27413     /**
27414      * Adds a object form elements (using the xtype property as the factory method.)
27415      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27416      * @param {Object} config 
27417      */
27418     addxtype : function(o)
27419     {
27420         // create the lement.
27421         o.form = this.form;
27422         var fe = Roo.factory(o, Roo.form);
27423         this.form.allItems.push(fe);
27424         this.stack.push(fe);
27425         
27426         if (fe.isFormField) {
27427             this.form.items.add(fe);
27428         }
27429          
27430         return fe;
27431     }
27432 });
27433
27434 /**
27435  * @class Roo.form.Column
27436  * @extends Roo.form.Layout
27437  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27438  * @constructor
27439  * @param {Object} config Configuration options
27440  */
27441 Roo.form.Column = function(config){
27442     Roo.form.Column.superclass.constructor.call(this, config);
27443 };
27444
27445 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27446     /**
27447      * @cfg {Number/String} width
27448      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27449      */
27450     /**
27451      * @cfg {String/Object} autoCreate
27452      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27453      */
27454
27455     // private
27456     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27457
27458     // private
27459     onRender : function(ct, position){
27460         Roo.form.Column.superclass.onRender.call(this, ct, position);
27461         if(this.width){
27462             this.el.setWidth(this.width);
27463         }
27464     }
27465 });
27466
27467
27468 /**
27469  * @class Roo.form.Row
27470  * @extends Roo.form.Layout
27471  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27472  * @constructor
27473  * @param {Object} config Configuration options
27474  */
27475
27476  
27477 Roo.form.Row = function(config){
27478     Roo.form.Row.superclass.constructor.call(this, config);
27479 };
27480  
27481 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27482       /**
27483      * @cfg {Number/String} width
27484      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27485      */
27486     /**
27487      * @cfg {Number/String} height
27488      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27489      */
27490     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27491     
27492     padWidth : 20,
27493     // private
27494     onRender : function(ct, position){
27495         //console.log('row render');
27496         if(!this.rowTpl){
27497             var t = new Roo.Template(
27498                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27499                     '<label for="{0}" style="{2}">{1}{4}</label>',
27500                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27501                     '</div>',
27502                 '</div>'
27503             );
27504             t.disableFormats = true;
27505             t.compile();
27506             Roo.form.Layout.prototype.rowTpl = t;
27507         }
27508         this.fieldTpl = this.rowTpl;
27509         
27510         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27511         var labelWidth = 100;
27512         
27513         if ((this.labelAlign != 'top')) {
27514             if (typeof this.labelWidth == 'number') {
27515                 labelWidth = this.labelWidth
27516             }
27517             this.padWidth =  20 + labelWidth;
27518             
27519         }
27520         
27521         Roo.form.Column.superclass.onRender.call(this, ct, position);
27522         if(this.width){
27523             this.el.setWidth(this.width);
27524         }
27525         if(this.height){
27526             this.el.setHeight(this.height);
27527         }
27528     },
27529     
27530     // private
27531     renderField : function(f){
27532         f.fieldEl = this.fieldTpl.append(this.el, [
27533                f.id, f.fieldLabel,
27534                f.labelStyle||this.labelStyle||'',
27535                this.elementStyle||'',
27536                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27537                f.itemCls||this.itemCls||'',
27538                f.width ? f.width + this.padWidth : 160 + this.padWidth
27539        ],true);
27540     }
27541 });
27542  
27543
27544 /**
27545  * @class Roo.form.FieldSet
27546  * @extends Roo.form.Layout
27547  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27548  * @constructor
27549  * @param {Object} config Configuration options
27550  */
27551 Roo.form.FieldSet = function(config){
27552     Roo.form.FieldSet.superclass.constructor.call(this, config);
27553 };
27554
27555 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27556     /**
27557      * @cfg {String} legend
27558      * The text to display as the legend for the FieldSet (defaults to '')
27559      */
27560     /**
27561      * @cfg {String/Object} autoCreate
27562      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27563      */
27564
27565     // private
27566     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27567
27568     // private
27569     onRender : function(ct, position){
27570         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27571         if(this.legend){
27572             this.setLegend(this.legend);
27573         }
27574     },
27575
27576     // private
27577     setLegend : function(text){
27578         if(this.rendered){
27579             this.el.child('legend').update(text);
27580         }
27581     }
27582 });/*
27583  * Based on:
27584  * Ext JS Library 1.1.1
27585  * Copyright(c) 2006-2007, Ext JS, LLC.
27586  *
27587  * Originally Released Under LGPL - original licence link has changed is not relivant.
27588  *
27589  * Fork - LGPL
27590  * <script type="text/javascript">
27591  */
27592 /**
27593  * @class Roo.form.VTypes
27594  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27595  * @singleton
27596  */
27597 Roo.form.VTypes = function(){
27598     // closure these in so they are only created once.
27599     var alpha = /^[a-zA-Z_]+$/;
27600     var alphanum = /^[a-zA-Z0-9_]+$/;
27601     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27602     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27603
27604     // All these messages and functions are configurable
27605     return {
27606         /**
27607          * The function used to validate email addresses
27608          * @param {String} value The email address
27609          */
27610         'email' : function(v){
27611             return email.test(v);
27612         },
27613         /**
27614          * The error text to display when the email validation function returns false
27615          * @type String
27616          */
27617         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27618         /**
27619          * The keystroke filter mask to be applied on email input
27620          * @type RegExp
27621          */
27622         'emailMask' : /[a-z0-9_\.\-@]/i,
27623
27624         /**
27625          * The function used to validate URLs
27626          * @param {String} value The URL
27627          */
27628         'url' : function(v){
27629             return url.test(v);
27630         },
27631         /**
27632          * The error text to display when the url validation function returns false
27633          * @type String
27634          */
27635         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27636         
27637         /**
27638          * The function used to validate alpha values
27639          * @param {String} value The value
27640          */
27641         'alpha' : function(v){
27642             return alpha.test(v);
27643         },
27644         /**
27645          * The error text to display when the alpha validation function returns false
27646          * @type String
27647          */
27648         'alphaText' : 'This field should only contain letters and _',
27649         /**
27650          * The keystroke filter mask to be applied on alpha input
27651          * @type RegExp
27652          */
27653         'alphaMask' : /[a-z_]/i,
27654
27655         /**
27656          * The function used to validate alphanumeric values
27657          * @param {String} value The value
27658          */
27659         'alphanum' : function(v){
27660             return alphanum.test(v);
27661         },
27662         /**
27663          * The error text to display when the alphanumeric validation function returns false
27664          * @type String
27665          */
27666         'alphanumText' : 'This field should only contain letters, numbers and _',
27667         /**
27668          * The keystroke filter mask to be applied on alphanumeric input
27669          * @type RegExp
27670          */
27671         'alphanumMask' : /[a-z0-9_]/i
27672     };
27673 }();//<script type="text/javascript">
27674
27675 /**
27676  * @class Roo.form.FCKeditor
27677  * @extends Roo.form.TextArea
27678  * Wrapper around the FCKEditor http://www.fckeditor.net
27679  * @constructor
27680  * Creates a new FCKeditor
27681  * @param {Object} config Configuration options
27682  */
27683 Roo.form.FCKeditor = function(config){
27684     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27685     this.addEvents({
27686          /**
27687          * @event editorinit
27688          * Fired when the editor is initialized - you can add extra handlers here..
27689          * @param {FCKeditor} this
27690          * @param {Object} the FCK object.
27691          */
27692         editorinit : true
27693     });
27694     
27695     
27696 };
27697 Roo.form.FCKeditor.editors = { };
27698 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27699 {
27700     //defaultAutoCreate : {
27701     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27702     //},
27703     // private
27704     /**
27705      * @cfg {Object} fck options - see fck manual for details.
27706      */
27707     fckconfig : false,
27708     
27709     /**
27710      * @cfg {Object} fck toolbar set (Basic or Default)
27711      */
27712     toolbarSet : 'Basic',
27713     /**
27714      * @cfg {Object} fck BasePath
27715      */ 
27716     basePath : '/fckeditor/',
27717     
27718     
27719     frame : false,
27720     
27721     value : '',
27722     
27723    
27724     onRender : function(ct, position)
27725     {
27726         if(!this.el){
27727             this.defaultAutoCreate = {
27728                 tag: "textarea",
27729                 style:"width:300px;height:60px;",
27730                 autocomplete: "off"
27731             };
27732         }
27733         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27734         /*
27735         if(this.grow){
27736             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27737             if(this.preventScrollbars){
27738                 this.el.setStyle("overflow", "hidden");
27739             }
27740             this.el.setHeight(this.growMin);
27741         }
27742         */
27743         //console.log('onrender' + this.getId() );
27744         Roo.form.FCKeditor.editors[this.getId()] = this;
27745          
27746
27747         this.replaceTextarea() ;
27748         
27749     },
27750     
27751     getEditor : function() {
27752         return this.fckEditor;
27753     },
27754     /**
27755      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27756      * @param {Mixed} value The value to set
27757      */
27758     
27759     
27760     setValue : function(value)
27761     {
27762         //console.log('setValue: ' + value);
27763         
27764         if(typeof(value) == 'undefined') { // not sure why this is happending...
27765             return;
27766         }
27767         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27768         
27769         //if(!this.el || !this.getEditor()) {
27770         //    this.value = value;
27771             //this.setValue.defer(100,this,[value]);    
27772         //    return;
27773         //} 
27774         
27775         if(!this.getEditor()) {
27776             return;
27777         }
27778         
27779         this.getEditor().SetData(value);
27780         
27781         //
27782
27783     },
27784
27785     /**
27786      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27787      * @return {Mixed} value The field value
27788      */
27789     getValue : function()
27790     {
27791         
27792         if (this.frame && this.frame.dom.style.display == 'none') {
27793             return Roo.form.FCKeditor.superclass.getValue.call(this);
27794         }
27795         
27796         if(!this.el || !this.getEditor()) {
27797            
27798            // this.getValue.defer(100,this); 
27799             return this.value;
27800         }
27801        
27802         
27803         var value=this.getEditor().GetData();
27804         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27805         return Roo.form.FCKeditor.superclass.getValue.call(this);
27806         
27807
27808     },
27809
27810     /**
27811      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27812      * @return {Mixed} value The field value
27813      */
27814     getRawValue : function()
27815     {
27816         if (this.frame && this.frame.dom.style.display == 'none') {
27817             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27818         }
27819         
27820         if(!this.el || !this.getEditor()) {
27821             //this.getRawValue.defer(100,this); 
27822             return this.value;
27823             return;
27824         }
27825         
27826         
27827         
27828         var value=this.getEditor().GetData();
27829         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27830         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27831          
27832     },
27833     
27834     setSize : function(w,h) {
27835         
27836         
27837         
27838         //if (this.frame && this.frame.dom.style.display == 'none') {
27839         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27840         //    return;
27841         //}
27842         //if(!this.el || !this.getEditor()) {
27843         //    this.setSize.defer(100,this, [w,h]); 
27844         //    return;
27845         //}
27846         
27847         
27848         
27849         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27850         
27851         this.frame.dom.setAttribute('width', w);
27852         this.frame.dom.setAttribute('height', h);
27853         this.frame.setSize(w,h);
27854         
27855     },
27856     
27857     toggleSourceEdit : function(value) {
27858         
27859       
27860          
27861         this.el.dom.style.display = value ? '' : 'none';
27862         this.frame.dom.style.display = value ?  'none' : '';
27863         
27864     },
27865     
27866     
27867     focus: function(tag)
27868     {
27869         if (this.frame.dom.style.display == 'none') {
27870             return Roo.form.FCKeditor.superclass.focus.call(this);
27871         }
27872         if(!this.el || !this.getEditor()) {
27873             this.focus.defer(100,this, [tag]); 
27874             return;
27875         }
27876         
27877         
27878         
27879         
27880         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27881         this.getEditor().Focus();
27882         if (tgs.length) {
27883             if (!this.getEditor().Selection.GetSelection()) {
27884                 this.focus.defer(100,this, [tag]); 
27885                 return;
27886             }
27887             
27888             
27889             var r = this.getEditor().EditorDocument.createRange();
27890             r.setStart(tgs[0],0);
27891             r.setEnd(tgs[0],0);
27892             this.getEditor().Selection.GetSelection().removeAllRanges();
27893             this.getEditor().Selection.GetSelection().addRange(r);
27894             this.getEditor().Focus();
27895         }
27896         
27897     },
27898     
27899     
27900     
27901     replaceTextarea : function()
27902     {
27903         if ( document.getElementById( this.getId() + '___Frame' ) )
27904             return ;
27905         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27906         //{
27907             // We must check the elements firstly using the Id and then the name.
27908         var oTextarea = document.getElementById( this.getId() );
27909         
27910         var colElementsByName = document.getElementsByName( this.getId() ) ;
27911          
27912         oTextarea.style.display = 'none' ;
27913
27914         if ( oTextarea.tabIndex ) {            
27915             this.TabIndex = oTextarea.tabIndex ;
27916         }
27917         
27918         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27919         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27920         this.frame = Roo.get(this.getId() + '___Frame')
27921     },
27922     
27923     _getConfigHtml : function()
27924     {
27925         var sConfig = '' ;
27926
27927         for ( var o in this.fckconfig ) {
27928             sConfig += sConfig.length > 0  ? '&amp;' : '';
27929             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27930         }
27931
27932         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27933     },
27934     
27935     
27936     _getIFrameHtml : function()
27937     {
27938         var sFile = 'fckeditor.html' ;
27939         /* no idea what this is about..
27940         try
27941         {
27942             if ( (/fcksource=true/i).test( window.top.location.search ) )
27943                 sFile = 'fckeditor.original.html' ;
27944         }
27945         catch (e) { 
27946         */
27947
27948         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27949         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27950         
27951         
27952         var html = '<iframe id="' + this.getId() +
27953             '___Frame" src="' + sLink +
27954             '" width="' + this.width +
27955             '" height="' + this.height + '"' +
27956             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27957             ' frameborder="0" scrolling="no"></iframe>' ;
27958
27959         return html ;
27960     },
27961     
27962     _insertHtmlBefore : function( html, element )
27963     {
27964         if ( element.insertAdjacentHTML )       {
27965             // IE
27966             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27967         } else { // Gecko
27968             var oRange = document.createRange() ;
27969             oRange.setStartBefore( element ) ;
27970             var oFragment = oRange.createContextualFragment( html );
27971             element.parentNode.insertBefore( oFragment, element ) ;
27972         }
27973     }
27974     
27975     
27976   
27977     
27978     
27979     
27980     
27981
27982 });
27983
27984 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27985
27986 function FCKeditor_OnComplete(editorInstance){
27987     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27988     f.fckEditor = editorInstance;
27989     //console.log("loaded");
27990     f.fireEvent('editorinit', f, editorInstance);
27991
27992   
27993
27994  
27995
27996
27997
27998
27999
28000
28001
28002
28003
28004
28005
28006
28007
28008
28009
28010 //<script type="text/javascript">
28011 /**
28012  * @class Roo.form.GridField
28013  * @extends Roo.form.Field
28014  * Embed a grid (or editable grid into a form)
28015  * STATUS ALPHA
28016  * 
28017  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28018  * it needs 
28019  * xgrid.store = Roo.data.Store
28020  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28021  * xgrid.store.reader = Roo.data.JsonReader 
28022  * 
28023  * 
28024  * @constructor
28025  * Creates a new GridField
28026  * @param {Object} config Configuration options
28027  */
28028 Roo.form.GridField = function(config){
28029     Roo.form.GridField.superclass.constructor.call(this, config);
28030      
28031 };
28032
28033 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28034     /**
28035      * @cfg {Number} width  - used to restrict width of grid..
28036      */
28037     width : 100,
28038     /**
28039      * @cfg {Number} height - used to restrict height of grid..
28040      */
28041     height : 50,
28042      /**
28043      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28044          * 
28045          *}
28046      */
28047     xgrid : false, 
28048     /**
28049      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28050      * {tag: "input", type: "checkbox", autocomplete: "off"})
28051      */
28052    // defaultAutoCreate : { tag: 'div' },
28053     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28054     /**
28055      * @cfg {String} addTitle Text to include for adding a title.
28056      */
28057     addTitle : false,
28058     //
28059     onResize : function(){
28060         Roo.form.Field.superclass.onResize.apply(this, arguments);
28061     },
28062
28063     initEvents : function(){
28064         // Roo.form.Checkbox.superclass.initEvents.call(this);
28065         // has no events...
28066        
28067     },
28068
28069
28070     getResizeEl : function(){
28071         return this.wrap;
28072     },
28073
28074     getPositionEl : function(){
28075         return this.wrap;
28076     },
28077
28078     // private
28079     onRender : function(ct, position){
28080         
28081         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28082         var style = this.style;
28083         delete this.style;
28084         
28085         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28086         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28087         this.viewEl = this.wrap.createChild({ tag: 'div' });
28088         if (style) {
28089             this.viewEl.applyStyles(style);
28090         }
28091         if (this.width) {
28092             this.viewEl.setWidth(this.width);
28093         }
28094         if (this.height) {
28095             this.viewEl.setHeight(this.height);
28096         }
28097         //if(this.inputValue !== undefined){
28098         //this.setValue(this.value);
28099         
28100         
28101         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28102         
28103         
28104         this.grid.render();
28105         this.grid.getDataSource().on('remove', this.refreshValue, this);
28106         this.grid.getDataSource().on('update', this.refreshValue, this);
28107         this.grid.on('afteredit', this.refreshValue, this);
28108  
28109     },
28110      
28111     
28112     /**
28113      * Sets the value of the item. 
28114      * @param {String} either an object  or a string..
28115      */
28116     setValue : function(v){
28117         //this.value = v;
28118         v = v || []; // empty set..
28119         // this does not seem smart - it really only affects memoryproxy grids..
28120         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28121             var ds = this.grid.getDataSource();
28122             // assumes a json reader..
28123             var data = {}
28124             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28125             ds.loadData( data);
28126         }
28127         Roo.form.GridField.superclass.setValue.call(this, v);
28128         this.refreshValue();
28129         // should load data in the grid really....
28130     },
28131     
28132     // private
28133     refreshValue: function() {
28134          var val = [];
28135         this.grid.getDataSource().each(function(r) {
28136             val.push(r.data);
28137         });
28138         this.el.dom.value = Roo.encode(val);
28139     }
28140     
28141      
28142     
28143     
28144 });/*
28145  * Based on:
28146  * Ext JS Library 1.1.1
28147  * Copyright(c) 2006-2007, Ext JS, LLC.
28148  *
28149  * Originally Released Under LGPL - original licence link has changed is not relivant.
28150  *
28151  * Fork - LGPL
28152  * <script type="text/javascript">
28153  */
28154 /**
28155  * @class Roo.form.DisplayField
28156  * @extends Roo.form.Field
28157  * A generic Field to display non-editable data.
28158  * @constructor
28159  * Creates a new Display Field item.
28160  * @param {Object} config Configuration options
28161  */
28162 Roo.form.DisplayField = function(config){
28163     Roo.form.DisplayField.superclass.constructor.call(this, config);
28164     
28165 };
28166
28167 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28168     inputType:      'hidden',
28169     allowBlank:     true,
28170     readOnly:         true,
28171     
28172  
28173     /**
28174      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28175      */
28176     focusClass : undefined,
28177     /**
28178      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28179      */
28180     fieldClass: 'x-form-field',
28181     
28182      /**
28183      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28184      */
28185     valueRenderer: undefined,
28186     
28187     width: 100,
28188     /**
28189      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28190      * {tag: "input", type: "checkbox", autocomplete: "off"})
28191      */
28192      
28193  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28194
28195     onResize : function(){
28196         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28197         
28198     },
28199
28200     initEvents : function(){
28201         // Roo.form.Checkbox.superclass.initEvents.call(this);
28202         // has no events...
28203        
28204     },
28205
28206
28207     getResizeEl : function(){
28208         return this.wrap;
28209     },
28210
28211     getPositionEl : function(){
28212         return this.wrap;
28213     },
28214
28215     // private
28216     onRender : function(ct, position){
28217         
28218         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28219         //if(this.inputValue !== undefined){
28220         this.wrap = this.el.wrap();
28221         
28222         this.viewEl = this.wrap.createChild({ tag: 'div'});
28223         
28224         if (this.bodyStyle) {
28225             this.viewEl.applyStyles(this.bodyStyle);
28226         }
28227         //this.viewEl.setStyle('padding', '2px');
28228         
28229         this.setValue(this.value);
28230         
28231     },
28232 /*
28233     // private
28234     initValue : Roo.emptyFn,
28235
28236   */
28237
28238         // private
28239     onClick : function(){
28240         
28241     },
28242
28243     /**
28244      * Sets the checked state of the checkbox.
28245      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28246      */
28247     setValue : function(v){
28248         this.value = v;
28249         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28250         // this might be called before we have a dom element..
28251         if (!this.viewEl) {
28252             return;
28253         }
28254         this.viewEl.dom.innerHTML = html;
28255         Roo.form.DisplayField.superclass.setValue.call(this, v);
28256
28257     }
28258 });//<script type="text/javasscript">
28259  
28260
28261 /**
28262  * @class Roo.DDView
28263  * A DnD enabled version of Roo.View.
28264  * @param {Element/String} container The Element in which to create the View.
28265  * @param {String} tpl The template string used to create the markup for each element of the View
28266  * @param {Object} config The configuration properties. These include all the config options of
28267  * {@link Roo.View} plus some specific to this class.<br>
28268  * <p>
28269  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28270  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28271  * <p>
28272  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28273 .x-view-drag-insert-above {
28274         border-top:1px dotted #3366cc;
28275 }
28276 .x-view-drag-insert-below {
28277         border-bottom:1px dotted #3366cc;
28278 }
28279 </code></pre>
28280  * 
28281  */
28282  
28283 Roo.DDView = function(container, tpl, config) {
28284     Roo.DDView.superclass.constructor.apply(this, arguments);
28285     this.getEl().setStyle("outline", "0px none");
28286     this.getEl().unselectable();
28287     if (this.dragGroup) {
28288                 this.setDraggable(this.dragGroup.split(","));
28289     }
28290     if (this.dropGroup) {
28291                 this.setDroppable(this.dropGroup.split(","));
28292     }
28293     if (this.deletable) {
28294         this.setDeletable();
28295     }
28296     this.isDirtyFlag = false;
28297         this.addEvents({
28298                 "drop" : true
28299         });
28300 };
28301
28302 Roo.extend(Roo.DDView, Roo.View, {
28303 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28304 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28305 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28306 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28307
28308         isFormField: true,
28309
28310         reset: Roo.emptyFn,
28311         
28312         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28313
28314         validate: function() {
28315                 return true;
28316         },
28317         
28318         destroy: function() {
28319                 this.purgeListeners();
28320                 this.getEl.removeAllListeners();
28321                 this.getEl().remove();
28322                 if (this.dragZone) {
28323                         if (this.dragZone.destroy) {
28324                                 this.dragZone.destroy();
28325                         }
28326                 }
28327                 if (this.dropZone) {
28328                         if (this.dropZone.destroy) {
28329                                 this.dropZone.destroy();
28330                         }
28331                 }
28332         },
28333
28334 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28335         getName: function() {
28336                 return this.name;
28337         },
28338
28339 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28340         setValue: function(v) {
28341                 if (!this.store) {
28342                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28343                 }
28344                 var data = {};
28345                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28346                 this.store.proxy = new Roo.data.MemoryProxy(data);
28347                 this.store.load();
28348         },
28349
28350 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28351         getValue: function() {
28352                 var result = '(';
28353                 this.store.each(function(rec) {
28354                         result += rec.id + ',';
28355                 });
28356                 return result.substr(0, result.length - 1) + ')';
28357         },
28358         
28359         getIds: function() {
28360                 var i = 0, result = new Array(this.store.getCount());
28361                 this.store.each(function(rec) {
28362                         result[i++] = rec.id;
28363                 });
28364                 return result;
28365         },
28366         
28367         isDirty: function() {
28368                 return this.isDirtyFlag;
28369         },
28370
28371 /**
28372  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28373  *      whole Element becomes the target, and this causes the drop gesture to append.
28374  */
28375     getTargetFromEvent : function(e) {
28376                 var target = e.getTarget();
28377                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28378                 target = target.parentNode;
28379                 }
28380                 if (!target) {
28381                         target = this.el.dom.lastChild || this.el.dom;
28382                 }
28383                 return target;
28384     },
28385
28386 /**
28387  *      Create the drag data which consists of an object which has the property "ddel" as
28388  *      the drag proxy element. 
28389  */
28390     getDragData : function(e) {
28391         var target = this.findItemFromChild(e.getTarget());
28392                 if(target) {
28393                         this.handleSelection(e);
28394                         var selNodes = this.getSelectedNodes();
28395             var dragData = {
28396                 source: this,
28397                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28398                 nodes: selNodes,
28399                 records: []
28400                         };
28401                         var selectedIndices = this.getSelectedIndexes();
28402                         for (var i = 0; i < selectedIndices.length; i++) {
28403                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28404                         }
28405                         if (selNodes.length == 1) {
28406                                 dragData.ddel = target.cloneNode(true); // the div element
28407                         } else {
28408                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28409                                 div.className = 'multi-proxy';
28410                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28411                                         div.appendChild(selNodes[i].cloneNode(true));
28412                                 }
28413                                 dragData.ddel = div;
28414                         }
28415             //console.log(dragData)
28416             //console.log(dragData.ddel.innerHTML)
28417                         return dragData;
28418                 }
28419         //console.log('nodragData')
28420                 return false;
28421     },
28422     
28423 /**     Specify to which ddGroup items in this DDView may be dragged. */
28424     setDraggable: function(ddGroup) {
28425         if (ddGroup instanceof Array) {
28426                 Roo.each(ddGroup, this.setDraggable, this);
28427                 return;
28428         }
28429         if (this.dragZone) {
28430                 this.dragZone.addToGroup(ddGroup);
28431         } else {
28432                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28433                                 containerScroll: true,
28434                                 ddGroup: ddGroup 
28435
28436                         });
28437 //                      Draggability implies selection. DragZone's mousedown selects the element.
28438                         if (!this.multiSelect) { this.singleSelect = true; }
28439
28440 //                      Wire the DragZone's handlers up to methods in *this*
28441                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28442                 }
28443     },
28444
28445 /**     Specify from which ddGroup this DDView accepts drops. */
28446     setDroppable: function(ddGroup) {
28447         if (ddGroup instanceof Array) {
28448                 Roo.each(ddGroup, this.setDroppable, this);
28449                 return;
28450         }
28451         if (this.dropZone) {
28452                 this.dropZone.addToGroup(ddGroup);
28453         } else {
28454                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28455                                 containerScroll: true,
28456                                 ddGroup: ddGroup
28457                         });
28458
28459 //                      Wire the DropZone's handlers up to methods in *this*
28460                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28461                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28462                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28463                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28464                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28465                 }
28466     },
28467
28468 /**     Decide whether to drop above or below a View node. */
28469     getDropPoint : function(e, n, dd){
28470         if (n == this.el.dom) { return "above"; }
28471                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28472                 var c = t + (b - t) / 2;
28473                 var y = Roo.lib.Event.getPageY(e);
28474                 if(y <= c) {
28475                         return "above";
28476                 }else{
28477                         return "below";
28478                 }
28479     },
28480
28481     onNodeEnter : function(n, dd, e, data){
28482                 return false;
28483     },
28484     
28485     onNodeOver : function(n, dd, e, data){
28486                 var pt = this.getDropPoint(e, n, dd);
28487                 // set the insert point style on the target node
28488                 var dragElClass = this.dropNotAllowed;
28489                 if (pt) {
28490                         var targetElClass;
28491                         if (pt == "above"){
28492                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28493                                 targetElClass = "x-view-drag-insert-above";
28494                         } else {
28495                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28496                                 targetElClass = "x-view-drag-insert-below";
28497                         }
28498                         if (this.lastInsertClass != targetElClass){
28499                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28500                                 this.lastInsertClass = targetElClass;
28501                         }
28502                 }
28503                 return dragElClass;
28504         },
28505
28506     onNodeOut : function(n, dd, e, data){
28507                 this.removeDropIndicators(n);
28508     },
28509
28510     onNodeDrop : function(n, dd, e, data){
28511         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28512                 return false;
28513         }
28514         var pt = this.getDropPoint(e, n, dd);
28515                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28516                 if (pt == "below") { insertAt++; }
28517                 for (var i = 0; i < data.records.length; i++) {
28518                         var r = data.records[i];
28519                         var dup = this.store.getById(r.id);
28520                         if (dup && (dd != this.dragZone)) {
28521                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28522                         } else {
28523                                 if (data.copy) {
28524                                         this.store.insert(insertAt++, r.copy());
28525                                 } else {
28526                                         data.source.isDirtyFlag = true;
28527                                         r.store.remove(r);
28528                                         this.store.insert(insertAt++, r);
28529                                 }
28530                                 this.isDirtyFlag = true;
28531                         }
28532                 }
28533                 this.dragZone.cachedTarget = null;
28534                 return true;
28535     },
28536
28537     removeDropIndicators : function(n){
28538                 if(n){
28539                         Roo.fly(n).removeClass([
28540                                 "x-view-drag-insert-above",
28541                                 "x-view-drag-insert-below"]);
28542                         this.lastInsertClass = "_noclass";
28543                 }
28544     },
28545
28546 /**
28547  *      Utility method. Add a delete option to the DDView's context menu.
28548  *      @param {String} imageUrl The URL of the "delete" icon image.
28549  */
28550         setDeletable: function(imageUrl) {
28551                 if (!this.singleSelect && !this.multiSelect) {
28552                         this.singleSelect = true;
28553                 }
28554                 var c = this.getContextMenu();
28555                 this.contextMenu.on("itemclick", function(item) {
28556                         switch (item.id) {
28557                                 case "delete":
28558                                         this.remove(this.getSelectedIndexes());
28559                                         break;
28560                         }
28561                 }, this);
28562                 this.contextMenu.add({
28563                         icon: imageUrl,
28564                         id: "delete",
28565                         text: 'Delete'
28566                 });
28567         },
28568         
28569 /**     Return the context menu for this DDView. */
28570         getContextMenu: function() {
28571                 if (!this.contextMenu) {
28572 //                      Create the View's context menu
28573                         this.contextMenu = new Roo.menu.Menu({
28574                                 id: this.id + "-contextmenu"
28575                         });
28576                         this.el.on("contextmenu", this.showContextMenu, this);
28577                 }
28578                 return this.contextMenu;
28579         },
28580         
28581         disableContextMenu: function() {
28582                 if (this.contextMenu) {
28583                         this.el.un("contextmenu", this.showContextMenu, this);
28584                 }
28585         },
28586
28587         showContextMenu: function(e, item) {
28588         item = this.findItemFromChild(e.getTarget());
28589                 if (item) {
28590                         e.stopEvent();
28591                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28592                         this.contextMenu.showAt(e.getXY());
28593             }
28594     },
28595
28596 /**
28597  *      Remove {@link Roo.data.Record}s at the specified indices.
28598  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28599  */
28600     remove: function(selectedIndices) {
28601                 selectedIndices = [].concat(selectedIndices);
28602                 for (var i = 0; i < selectedIndices.length; i++) {
28603                         var rec = this.store.getAt(selectedIndices[i]);
28604                         this.store.remove(rec);
28605                 }
28606     },
28607
28608 /**
28609  *      Double click fires the event, but also, if this is draggable, and there is only one other
28610  *      related DropZone, it transfers the selected node.
28611  */
28612     onDblClick : function(e){
28613         var item = this.findItemFromChild(e.getTarget());
28614         if(item){
28615             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28616                 return false;
28617             }
28618             if (this.dragGroup) {
28619                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28620                     while (targets.indexOf(this.dropZone) > -1) {
28621                             targets.remove(this.dropZone);
28622                                 }
28623                     if (targets.length == 1) {
28624                                         this.dragZone.cachedTarget = null;
28625                         var el = Roo.get(targets[0].getEl());
28626                         var box = el.getBox(true);
28627                         targets[0].onNodeDrop(el.dom, {
28628                                 target: el.dom,
28629                                 xy: [box.x, box.y + box.height - 1]
28630                         }, null, this.getDragData(e));
28631                     }
28632                 }
28633         }
28634     },
28635     
28636     handleSelection: function(e) {
28637                 this.dragZone.cachedTarget = null;
28638         var item = this.findItemFromChild(e.getTarget());
28639         if (!item) {
28640                 this.clearSelections(true);
28641                 return;
28642         }
28643                 if (item && (this.multiSelect || this.singleSelect)){
28644                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28645                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28646                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28647                                 this.unselect(item);
28648                         } else {
28649                                 this.select(item, this.multiSelect && e.ctrlKey);
28650                                 this.lastSelection = item;
28651                         }
28652                 }
28653     },
28654
28655     onItemClick : function(item, index, e){
28656                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28657                         return false;
28658                 }
28659                 return true;
28660     },
28661
28662     unselect : function(nodeInfo, suppressEvent){
28663                 var node = this.getNode(nodeInfo);
28664                 if(node && this.isSelected(node)){
28665                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28666                                 Roo.fly(node).removeClass(this.selectedClass);
28667                                 this.selections.remove(node);
28668                                 if(!suppressEvent){
28669                                         this.fireEvent("selectionchange", this, this.selections);
28670                                 }
28671                         }
28672                 }
28673     }
28674 });
28675 /*
28676  * Based on:
28677  * Ext JS Library 1.1.1
28678  * Copyright(c) 2006-2007, Ext JS, LLC.
28679  *
28680  * Originally Released Under LGPL - original licence link has changed is not relivant.
28681  *
28682  * Fork - LGPL
28683  * <script type="text/javascript">
28684  */
28685  
28686 /**
28687  * @class Roo.LayoutManager
28688  * @extends Roo.util.Observable
28689  * Base class for layout managers.
28690  */
28691 Roo.LayoutManager = function(container, config){
28692     Roo.LayoutManager.superclass.constructor.call(this);
28693     this.el = Roo.get(container);
28694     // ie scrollbar fix
28695     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28696         document.body.scroll = "no";
28697     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28698         this.el.position('relative');
28699     }
28700     this.id = this.el.id;
28701     this.el.addClass("x-layout-container");
28702     /** false to disable window resize monitoring @type Boolean */
28703     this.monitorWindowResize = true;
28704     this.regions = {};
28705     this.addEvents({
28706         /**
28707          * @event layout
28708          * Fires when a layout is performed. 
28709          * @param {Roo.LayoutManager} this
28710          */
28711         "layout" : true,
28712         /**
28713          * @event regionresized
28714          * Fires when the user resizes a region. 
28715          * @param {Roo.LayoutRegion} region The resized region
28716          * @param {Number} newSize The new size (width for east/west, height for north/south)
28717          */
28718         "regionresized" : true,
28719         /**
28720          * @event regioncollapsed
28721          * Fires when a region is collapsed. 
28722          * @param {Roo.LayoutRegion} region The collapsed region
28723          */
28724         "regioncollapsed" : true,
28725         /**
28726          * @event regionexpanded
28727          * Fires when a region is expanded.  
28728          * @param {Roo.LayoutRegion} region The expanded region
28729          */
28730         "regionexpanded" : true
28731     });
28732     this.updating = false;
28733     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28734 };
28735
28736 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28737     /**
28738      * Returns true if this layout is currently being updated
28739      * @return {Boolean}
28740      */
28741     isUpdating : function(){
28742         return this.updating; 
28743     },
28744     
28745     /**
28746      * Suspend the LayoutManager from doing auto-layouts while
28747      * making multiple add or remove calls
28748      */
28749     beginUpdate : function(){
28750         this.updating = true;    
28751     },
28752     
28753     /**
28754      * Restore auto-layouts and optionally disable the manager from performing a layout
28755      * @param {Boolean} noLayout true to disable a layout update 
28756      */
28757     endUpdate : function(noLayout){
28758         this.updating = false;
28759         if(!noLayout){
28760             this.layout();
28761         }    
28762     },
28763     
28764     layout: function(){
28765         
28766     },
28767     
28768     onRegionResized : function(region, newSize){
28769         this.fireEvent("regionresized", region, newSize);
28770         this.layout();
28771     },
28772     
28773     onRegionCollapsed : function(region){
28774         this.fireEvent("regioncollapsed", region);
28775     },
28776     
28777     onRegionExpanded : function(region){
28778         this.fireEvent("regionexpanded", region);
28779     },
28780         
28781     /**
28782      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28783      * performs box-model adjustments.
28784      * @return {Object} The size as an object {width: (the width), height: (the height)}
28785      */
28786     getViewSize : function(){
28787         var size;
28788         if(this.el.dom != document.body){
28789             size = this.el.getSize();
28790         }else{
28791             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28792         }
28793         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28794         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28795         return size;
28796     },
28797     
28798     /**
28799      * Returns the Element this layout is bound to.
28800      * @return {Roo.Element}
28801      */
28802     getEl : function(){
28803         return this.el;
28804     },
28805     
28806     /**
28807      * Returns the specified region.
28808      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28809      * @return {Roo.LayoutRegion}
28810      */
28811     getRegion : function(target){
28812         return this.regions[target.toLowerCase()];
28813     },
28814     
28815     onWindowResize : function(){
28816         if(this.monitorWindowResize){
28817             this.layout();
28818         }
28819     }
28820 });/*
28821  * Based on:
28822  * Ext JS Library 1.1.1
28823  * Copyright(c) 2006-2007, Ext JS, LLC.
28824  *
28825  * Originally Released Under LGPL - original licence link has changed is not relivant.
28826  *
28827  * Fork - LGPL
28828  * <script type="text/javascript">
28829  */
28830 /**
28831  * @class Roo.BorderLayout
28832  * @extends Roo.LayoutManager
28833  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28834  * please see: <br><br>
28835  * <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>
28836  * <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>
28837  * Example:
28838  <pre><code>
28839  var layout = new Roo.BorderLayout(document.body, {
28840     north: {
28841         initialSize: 25,
28842         titlebar: false
28843     },
28844     west: {
28845         split:true,
28846         initialSize: 200,
28847         minSize: 175,
28848         maxSize: 400,
28849         titlebar: true,
28850         collapsible: true
28851     },
28852     east: {
28853         split:true,
28854         initialSize: 202,
28855         minSize: 175,
28856         maxSize: 400,
28857         titlebar: true,
28858         collapsible: true
28859     },
28860     south: {
28861         split:true,
28862         initialSize: 100,
28863         minSize: 100,
28864         maxSize: 200,
28865         titlebar: true,
28866         collapsible: true
28867     },
28868     center: {
28869         titlebar: true,
28870         autoScroll:true,
28871         resizeTabs: true,
28872         minTabWidth: 50,
28873         preferredTabWidth: 150
28874     }
28875 });
28876
28877 // shorthand
28878 var CP = Roo.ContentPanel;
28879
28880 layout.beginUpdate();
28881 layout.add("north", new CP("north", "North"));
28882 layout.add("south", new CP("south", {title: "South", closable: true}));
28883 layout.add("west", new CP("west", {title: "West"}));
28884 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28885 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28886 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28887 layout.getRegion("center").showPanel("center1");
28888 layout.endUpdate();
28889 </code></pre>
28890
28891 <b>The container the layout is rendered into can be either the body element or any other element.
28892 If it is not the body element, the container needs to either be an absolute positioned element,
28893 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28894 the container size if it is not the body element.</b>
28895
28896 * @constructor
28897 * Create a new BorderLayout
28898 * @param {String/HTMLElement/Element} container The container this layout is bound to
28899 * @param {Object} config Configuration options
28900  */
28901 Roo.BorderLayout = function(container, config){
28902     config = config || {};
28903     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28904     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28905     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28906         var target = this.factory.validRegions[i];
28907         if(config[target]){
28908             this.addRegion(target, config[target]);
28909         }
28910     }
28911 };
28912
28913 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28914     /**
28915      * Creates and adds a new region if it doesn't already exist.
28916      * @param {String} target The target region key (north, south, east, west or center).
28917      * @param {Object} config The regions config object
28918      * @return {BorderLayoutRegion} The new region
28919      */
28920     addRegion : function(target, config){
28921         if(!this.regions[target]){
28922             var r = this.factory.create(target, this, config);
28923             this.bindRegion(target, r);
28924         }
28925         return this.regions[target];
28926     },
28927
28928     // private (kinda)
28929     bindRegion : function(name, r){
28930         this.regions[name] = r;
28931         r.on("visibilitychange", this.layout, this);
28932         r.on("paneladded", this.layout, this);
28933         r.on("panelremoved", this.layout, this);
28934         r.on("invalidated", this.layout, this);
28935         r.on("resized", this.onRegionResized, this);
28936         r.on("collapsed", this.onRegionCollapsed, this);
28937         r.on("expanded", this.onRegionExpanded, this);
28938     },
28939
28940     /**
28941      * Performs a layout update.
28942      */
28943     layout : function(){
28944         if(this.updating) return;
28945         var size = this.getViewSize();
28946         var w = size.width;
28947         var h = size.height;
28948         var centerW = w;
28949         var centerH = h;
28950         var centerY = 0;
28951         var centerX = 0;
28952         //var x = 0, y = 0;
28953
28954         var rs = this.regions;
28955         var north = rs["north"];
28956         var south = rs["south"]; 
28957         var west = rs["west"];
28958         var east = rs["east"];
28959         var center = rs["center"];
28960         //if(this.hideOnLayout){ // not supported anymore
28961             //c.el.setStyle("display", "none");
28962         //}
28963         if(north && north.isVisible()){
28964             var b = north.getBox();
28965             var m = north.getMargins();
28966             b.width = w - (m.left+m.right);
28967             b.x = m.left;
28968             b.y = m.top;
28969             centerY = b.height + b.y + m.bottom;
28970             centerH -= centerY;
28971             north.updateBox(this.safeBox(b));
28972         }
28973         if(south && south.isVisible()){
28974             var b = south.getBox();
28975             var m = south.getMargins();
28976             b.width = w - (m.left+m.right);
28977             b.x = m.left;
28978             var totalHeight = (b.height + m.top + m.bottom);
28979             b.y = h - totalHeight + m.top;
28980             centerH -= totalHeight;
28981             south.updateBox(this.safeBox(b));
28982         }
28983         if(west && west.isVisible()){
28984             var b = west.getBox();
28985             var m = west.getMargins();
28986             b.height = centerH - (m.top+m.bottom);
28987             b.x = m.left;
28988             b.y = centerY + m.top;
28989             var totalWidth = (b.width + m.left + m.right);
28990             centerX += totalWidth;
28991             centerW -= totalWidth;
28992             west.updateBox(this.safeBox(b));
28993         }
28994         if(east && east.isVisible()){
28995             var b = east.getBox();
28996             var m = east.getMargins();
28997             b.height = centerH - (m.top+m.bottom);
28998             var totalWidth = (b.width + m.left + m.right);
28999             b.x = w - totalWidth + m.left;
29000             b.y = centerY + m.top;
29001             centerW -= totalWidth;
29002             east.updateBox(this.safeBox(b));
29003         }
29004         if(center){
29005             var m = center.getMargins();
29006             var centerBox = {
29007                 x: centerX + m.left,
29008                 y: centerY + m.top,
29009                 width: centerW - (m.left+m.right),
29010                 height: centerH - (m.top+m.bottom)
29011             };
29012             //if(this.hideOnLayout){
29013                 //center.el.setStyle("display", "block");
29014             //}
29015             center.updateBox(this.safeBox(centerBox));
29016         }
29017         this.el.repaint();
29018         this.fireEvent("layout", this);
29019     },
29020
29021     // private
29022     safeBox : function(box){
29023         box.width = Math.max(0, box.width);
29024         box.height = Math.max(0, box.height);
29025         return box;
29026     },
29027
29028     /**
29029      * Adds a ContentPanel (or subclass) to this layout.
29030      * @param {String} target The target region key (north, south, east, west or center).
29031      * @param {Roo.ContentPanel} panel The panel to add
29032      * @return {Roo.ContentPanel} The added panel
29033      */
29034     add : function(target, panel){
29035          
29036         target = target.toLowerCase();
29037         return this.regions[target].add(panel);
29038     },
29039
29040     /**
29041      * Remove a ContentPanel (or subclass) to this layout.
29042      * @param {String} target The target region key (north, south, east, west or center).
29043      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29044      * @return {Roo.ContentPanel} The removed panel
29045      */
29046     remove : function(target, panel){
29047         target = target.toLowerCase();
29048         return this.regions[target].remove(panel);
29049     },
29050
29051     /**
29052      * Searches all regions for a panel with the specified id
29053      * @param {String} panelId
29054      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29055      */
29056     findPanel : function(panelId){
29057         var rs = this.regions;
29058         for(var target in rs){
29059             if(typeof rs[target] != "function"){
29060                 var p = rs[target].getPanel(panelId);
29061                 if(p){
29062                     return p;
29063                 }
29064             }
29065         }
29066         return null;
29067     },
29068
29069     /**
29070      * Searches all regions for a panel with the specified id and activates (shows) it.
29071      * @param {String/ContentPanel} panelId The panels id or the panel itself
29072      * @return {Roo.ContentPanel} The shown panel or null
29073      */
29074     showPanel : function(panelId) {
29075       var rs = this.regions;
29076       for(var target in rs){
29077          var r = rs[target];
29078          if(typeof r != "function"){
29079             if(r.hasPanel(panelId)){
29080                return r.showPanel(panelId);
29081             }
29082          }
29083       }
29084       return null;
29085    },
29086
29087    /**
29088      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29089      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29090      */
29091     restoreState : function(provider){
29092         if(!provider){
29093             provider = Roo.state.Manager;
29094         }
29095         var sm = new Roo.LayoutStateManager();
29096         sm.init(this, provider);
29097     },
29098
29099     /**
29100      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29101      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29102      * a valid ContentPanel config object.  Example:
29103      * <pre><code>
29104 // Create the main layout
29105 var layout = new Roo.BorderLayout('main-ct', {
29106     west: {
29107         split:true,
29108         minSize: 175,
29109         titlebar: true
29110     },
29111     center: {
29112         title:'Components'
29113     }
29114 }, 'main-ct');
29115
29116 // Create and add multiple ContentPanels at once via configs
29117 layout.batchAdd({
29118    west: {
29119        id: 'source-files',
29120        autoCreate:true,
29121        title:'Ext Source Files',
29122        autoScroll:true,
29123        fitToFrame:true
29124    },
29125    center : {
29126        el: cview,
29127        autoScroll:true,
29128        fitToFrame:true,
29129        toolbar: tb,
29130        resizeEl:'cbody'
29131    }
29132 });
29133 </code></pre>
29134      * @param {Object} regions An object containing ContentPanel configs by region name
29135      */
29136     batchAdd : function(regions){
29137         this.beginUpdate();
29138         for(var rname in regions){
29139             var lr = this.regions[rname];
29140             if(lr){
29141                 this.addTypedPanels(lr, regions[rname]);
29142             }
29143         }
29144         this.endUpdate();
29145     },
29146
29147     // private
29148     addTypedPanels : function(lr, ps){
29149         if(typeof ps == 'string'){
29150             lr.add(new Roo.ContentPanel(ps));
29151         }
29152         else if(ps instanceof Array){
29153             for(var i =0, len = ps.length; i < len; i++){
29154                 this.addTypedPanels(lr, ps[i]);
29155             }
29156         }
29157         else if(!ps.events){ // raw config?
29158             var el = ps.el;
29159             delete ps.el; // prevent conflict
29160             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29161         }
29162         else {  // panel object assumed!
29163             lr.add(ps);
29164         }
29165     },
29166     /**
29167      * Adds a xtype elements to the layout.
29168      * <pre><code>
29169
29170 layout.addxtype({
29171        xtype : 'ContentPanel',
29172        region: 'west',
29173        items: [ .... ]
29174    }
29175 );
29176
29177 layout.addxtype({
29178         xtype : 'NestedLayoutPanel',
29179         region: 'west',
29180         layout: {
29181            center: { },
29182            west: { }   
29183         },
29184         items : [ ... list of content panels or nested layout panels.. ]
29185    }
29186 );
29187 </code></pre>
29188      * @param {Object} cfg Xtype definition of item to add.
29189      */
29190     addxtype : function(cfg)
29191     {
29192         // basically accepts a pannel...
29193         // can accept a layout region..!?!?
29194        // console.log('BorderLayout add ' + cfg.xtype)
29195         
29196         if (!cfg.xtype.match(/Panel$/)) {
29197             return false;
29198         }
29199         var ret = false;
29200         var region = cfg.region;
29201         delete cfg.region;
29202         
29203           
29204         var xitems = [];
29205         if (cfg.items) {
29206             xitems = cfg.items;
29207             delete cfg.items;
29208         }
29209         
29210         
29211         switch(cfg.xtype) 
29212         {
29213             case 'ContentPanel':  // ContentPanel (el, cfg)
29214             case 'ScrollPanel':  // ContentPanel (el, cfg)
29215                 if(cfg.autoCreate) {
29216                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29217                 } else {
29218                     var el = this.el.createChild();
29219                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29220                 }
29221                 
29222                 this.add(region, ret);
29223                 break;
29224             
29225             
29226             case 'TreePanel': // our new panel!
29227                 cfg.el = this.el.createChild();
29228                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29229                 this.add(region, ret);
29230                 break;
29231             
29232             case 'NestedLayoutPanel': 
29233                 // create a new Layout (which is  a Border Layout...
29234                 var el = this.el.createChild();
29235                 var clayout = cfg.layout;
29236                 delete cfg.layout;
29237                 clayout.items   = clayout.items  || [];
29238                 // replace this exitems with the clayout ones..
29239                 xitems = clayout.items;
29240                  
29241                 
29242                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29243                     cfg.background = false;
29244                 }
29245                 var layout = new Roo.BorderLayout(el, clayout);
29246                 
29247                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29248                 //console.log('adding nested layout panel '  + cfg.toSource());
29249                 this.add(region, ret);
29250                 
29251                 break;
29252                 
29253             case 'GridPanel': 
29254             
29255                 // needs grid and region
29256                 
29257                 //var el = this.getRegion(region).el.createChild();
29258                 var el = this.el.createChild();
29259                 // create the grid first...
29260                 
29261                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29262                 delete cfg.grid;
29263                 if (region == 'center' && this.active ) {
29264                     cfg.background = false;
29265                 }
29266                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29267                 
29268                 this.add(region, ret);
29269                 if (cfg.background) {
29270                     ret.on('activate', function(gp) {
29271                         if (!gp.grid.rendered) {
29272                             gp.grid.render();
29273                         }
29274                     });
29275                 } else {
29276                     grid.render();
29277                 }
29278                 break;
29279            
29280                
29281                 
29282                 
29283             default: 
29284                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29285                 return;
29286              // GridPanel (grid, cfg)
29287             
29288         }
29289         this.beginUpdate();
29290         // add children..
29291         Roo.each(xitems, function(i)  {
29292             ret.addxtype(i);
29293         });
29294         this.endUpdate();
29295         return ret;
29296         
29297     }
29298 });
29299
29300 /**
29301  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29302  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29303  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29304  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29305  * <pre><code>
29306 // shorthand
29307 var CP = Roo.ContentPanel;
29308
29309 var layout = Roo.BorderLayout.create({
29310     north: {
29311         initialSize: 25,
29312         titlebar: false,
29313         panels: [new CP("north", "North")]
29314     },
29315     west: {
29316         split:true,
29317         initialSize: 200,
29318         minSize: 175,
29319         maxSize: 400,
29320         titlebar: true,
29321         collapsible: true,
29322         panels: [new CP("west", {title: "West"})]
29323     },
29324     east: {
29325         split:true,
29326         initialSize: 202,
29327         minSize: 175,
29328         maxSize: 400,
29329         titlebar: true,
29330         collapsible: true,
29331         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29332     },
29333     south: {
29334         split:true,
29335         initialSize: 100,
29336         minSize: 100,
29337         maxSize: 200,
29338         titlebar: true,
29339         collapsible: true,
29340         panels: [new CP("south", {title: "South", closable: true})]
29341     },
29342     center: {
29343         titlebar: true,
29344         autoScroll:true,
29345         resizeTabs: true,
29346         minTabWidth: 50,
29347         preferredTabWidth: 150,
29348         panels: [
29349             new CP("center1", {title: "Close Me", closable: true}),
29350             new CP("center2", {title: "Center Panel", closable: false})
29351         ]
29352     }
29353 }, document.body);
29354
29355 layout.getRegion("center").showPanel("center1");
29356 </code></pre>
29357  * @param config
29358  * @param targetEl
29359  */
29360 Roo.BorderLayout.create = function(config, targetEl){
29361     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29362     layout.beginUpdate();
29363     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29364     for(var j = 0, jlen = regions.length; j < jlen; j++){
29365         var lr = regions[j];
29366         if(layout.regions[lr] && config[lr].panels){
29367             var r = layout.regions[lr];
29368             var ps = config[lr].panels;
29369             layout.addTypedPanels(r, ps);
29370         }
29371     }
29372     layout.endUpdate();
29373     return layout;
29374 };
29375
29376 // private
29377 Roo.BorderLayout.RegionFactory = {
29378     // private
29379     validRegions : ["north","south","east","west","center"],
29380
29381     // private
29382     create : function(target, mgr, config){
29383         target = target.toLowerCase();
29384         if(config.lightweight || config.basic){
29385             return new Roo.BasicLayoutRegion(mgr, config, target);
29386         }
29387         switch(target){
29388             case "north":
29389                 return new Roo.NorthLayoutRegion(mgr, config);
29390             case "south":
29391                 return new Roo.SouthLayoutRegion(mgr, config);
29392             case "east":
29393                 return new Roo.EastLayoutRegion(mgr, config);
29394             case "west":
29395                 return new Roo.WestLayoutRegion(mgr, config);
29396             case "center":
29397                 return new Roo.CenterLayoutRegion(mgr, config);
29398         }
29399         throw 'Layout region "'+target+'" not supported.';
29400     }
29401 };/*
29402  * Based on:
29403  * Ext JS Library 1.1.1
29404  * Copyright(c) 2006-2007, Ext JS, LLC.
29405  *
29406  * Originally Released Under LGPL - original licence link has changed is not relivant.
29407  *
29408  * Fork - LGPL
29409  * <script type="text/javascript">
29410  */
29411  
29412 /**
29413  * @class Roo.BasicLayoutRegion
29414  * @extends Roo.util.Observable
29415  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29416  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29417  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29418  */
29419 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29420     this.mgr = mgr;
29421     this.position  = pos;
29422     this.events = {
29423         /**
29424          * @scope Roo.BasicLayoutRegion
29425          */
29426         
29427         /**
29428          * @event beforeremove
29429          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29430          * @param {Roo.LayoutRegion} this
29431          * @param {Roo.ContentPanel} panel The panel
29432          * @param {Object} e The cancel event object
29433          */
29434         "beforeremove" : true,
29435         /**
29436          * @event invalidated
29437          * Fires when the layout for this region is changed.
29438          * @param {Roo.LayoutRegion} this
29439          */
29440         "invalidated" : true,
29441         /**
29442          * @event visibilitychange
29443          * Fires when this region is shown or hidden 
29444          * @param {Roo.LayoutRegion} this
29445          * @param {Boolean} visibility true or false
29446          */
29447         "visibilitychange" : true,
29448         /**
29449          * @event paneladded
29450          * Fires when a panel is added. 
29451          * @param {Roo.LayoutRegion} this
29452          * @param {Roo.ContentPanel} panel The panel
29453          */
29454         "paneladded" : true,
29455         /**
29456          * @event panelremoved
29457          * Fires when a panel is removed. 
29458          * @param {Roo.LayoutRegion} this
29459          * @param {Roo.ContentPanel} panel The panel
29460          */
29461         "panelremoved" : true,
29462         /**
29463          * @event collapsed
29464          * Fires when this region is collapsed.
29465          * @param {Roo.LayoutRegion} this
29466          */
29467         "collapsed" : true,
29468         /**
29469          * @event expanded
29470          * Fires when this region is expanded.
29471          * @param {Roo.LayoutRegion} this
29472          */
29473         "expanded" : true,
29474         /**
29475          * @event slideshow
29476          * Fires when this region is slid into view.
29477          * @param {Roo.LayoutRegion} this
29478          */
29479         "slideshow" : true,
29480         /**
29481          * @event slidehide
29482          * Fires when this region slides out of view. 
29483          * @param {Roo.LayoutRegion} this
29484          */
29485         "slidehide" : true,
29486         /**
29487          * @event panelactivated
29488          * Fires when a panel is activated. 
29489          * @param {Roo.LayoutRegion} this
29490          * @param {Roo.ContentPanel} panel The activated panel
29491          */
29492         "panelactivated" : true,
29493         /**
29494          * @event resized
29495          * Fires when the user resizes this region. 
29496          * @param {Roo.LayoutRegion} this
29497          * @param {Number} newSize The new size (width for east/west, height for north/south)
29498          */
29499         "resized" : true
29500     };
29501     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29502     this.panels = new Roo.util.MixedCollection();
29503     this.panels.getKey = this.getPanelId.createDelegate(this);
29504     this.box = null;
29505     this.activePanel = null;
29506     // ensure listeners are added...
29507     
29508     if (config.listeners || config.events) {
29509         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29510             listeners : config.listeners || {},
29511             events : config.events || {}
29512         });
29513     }
29514     
29515     if(skipConfig !== true){
29516         this.applyConfig(config);
29517     }
29518 };
29519
29520 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29521     getPanelId : function(p){
29522         return p.getId();
29523     },
29524     
29525     applyConfig : function(config){
29526         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29527         this.config = config;
29528         
29529     },
29530     
29531     /**
29532      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29533      * the width, for horizontal (north, south) the height.
29534      * @param {Number} newSize The new width or height
29535      */
29536     resizeTo : function(newSize){
29537         var el = this.el ? this.el :
29538                  (this.activePanel ? this.activePanel.getEl() : null);
29539         if(el){
29540             switch(this.position){
29541                 case "east":
29542                 case "west":
29543                     el.setWidth(newSize);
29544                     this.fireEvent("resized", this, newSize);
29545                 break;
29546                 case "north":
29547                 case "south":
29548                     el.setHeight(newSize);
29549                     this.fireEvent("resized", this, newSize);
29550                 break;                
29551             }
29552         }
29553     },
29554     
29555     getBox : function(){
29556         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29557     },
29558     
29559     getMargins : function(){
29560         return this.margins;
29561     },
29562     
29563     updateBox : function(box){
29564         this.box = box;
29565         var el = this.activePanel.getEl();
29566         el.dom.style.left = box.x + "px";
29567         el.dom.style.top = box.y + "px";
29568         this.activePanel.setSize(box.width, box.height);
29569     },
29570     
29571     /**
29572      * Returns the container element for this region.
29573      * @return {Roo.Element}
29574      */
29575     getEl : function(){
29576         return this.activePanel;
29577     },
29578     
29579     /**
29580      * Returns true if this region is currently visible.
29581      * @return {Boolean}
29582      */
29583     isVisible : function(){
29584         return this.activePanel ? true : false;
29585     },
29586     
29587     setActivePanel : function(panel){
29588         panel = this.getPanel(panel);
29589         if(this.activePanel && this.activePanel != panel){
29590             this.activePanel.setActiveState(false);
29591             this.activePanel.getEl().setLeftTop(-10000,-10000);
29592         }
29593         this.activePanel = panel;
29594         panel.setActiveState(true);
29595         if(this.box){
29596             panel.setSize(this.box.width, this.box.height);
29597         }
29598         this.fireEvent("panelactivated", this, panel);
29599         this.fireEvent("invalidated");
29600     },
29601     
29602     /**
29603      * Show the specified panel.
29604      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29605      * @return {Roo.ContentPanel} The shown panel or null
29606      */
29607     showPanel : function(panel){
29608         if(panel = this.getPanel(panel)){
29609             this.setActivePanel(panel);
29610         }
29611         return panel;
29612     },
29613     
29614     /**
29615      * Get the active panel for this region.
29616      * @return {Roo.ContentPanel} The active panel or null
29617      */
29618     getActivePanel : function(){
29619         return this.activePanel;
29620     },
29621     
29622     /**
29623      * Add the passed ContentPanel(s)
29624      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29625      * @return {Roo.ContentPanel} The panel added (if only one was added)
29626      */
29627     add : function(panel){
29628         if(arguments.length > 1){
29629             for(var i = 0, len = arguments.length; i < len; i++) {
29630                 this.add(arguments[i]);
29631             }
29632             return null;
29633         }
29634         if(this.hasPanel(panel)){
29635             this.showPanel(panel);
29636             return panel;
29637         }
29638         var el = panel.getEl();
29639         if(el.dom.parentNode != this.mgr.el.dom){
29640             this.mgr.el.dom.appendChild(el.dom);
29641         }
29642         if(panel.setRegion){
29643             panel.setRegion(this);
29644         }
29645         this.panels.add(panel);
29646         el.setStyle("position", "absolute");
29647         if(!panel.background){
29648             this.setActivePanel(panel);
29649             if(this.config.initialSize && this.panels.getCount()==1){
29650                 this.resizeTo(this.config.initialSize);
29651             }
29652         }
29653         this.fireEvent("paneladded", this, panel);
29654         return panel;
29655     },
29656     
29657     /**
29658      * Returns true if the panel is in this region.
29659      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29660      * @return {Boolean}
29661      */
29662     hasPanel : function(panel){
29663         if(typeof panel == "object"){ // must be panel obj
29664             panel = panel.getId();
29665         }
29666         return this.getPanel(panel) ? true : false;
29667     },
29668     
29669     /**
29670      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29671      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29672      * @param {Boolean} preservePanel Overrides the config preservePanel option
29673      * @return {Roo.ContentPanel} The panel that was removed
29674      */
29675     remove : function(panel, preservePanel){
29676         panel = this.getPanel(panel);
29677         if(!panel){
29678             return null;
29679         }
29680         var e = {};
29681         this.fireEvent("beforeremove", this, panel, e);
29682         if(e.cancel === true){
29683             return null;
29684         }
29685         var panelId = panel.getId();
29686         this.panels.removeKey(panelId);
29687         return panel;
29688     },
29689     
29690     /**
29691      * Returns the panel specified or null if it's not in this region.
29692      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29693      * @return {Roo.ContentPanel}
29694      */
29695     getPanel : function(id){
29696         if(typeof id == "object"){ // must be panel obj
29697             return id;
29698         }
29699         return this.panels.get(id);
29700     },
29701     
29702     /**
29703      * Returns this regions position (north/south/east/west/center).
29704      * @return {String} 
29705      */
29706     getPosition: function(){
29707         return this.position;    
29708     }
29709 });/*
29710  * Based on:
29711  * Ext JS Library 1.1.1
29712  * Copyright(c) 2006-2007, Ext JS, LLC.
29713  *
29714  * Originally Released Under LGPL - original licence link has changed is not relivant.
29715  *
29716  * Fork - LGPL
29717  * <script type="text/javascript">
29718  */
29719  
29720 /**
29721  * @class Roo.LayoutRegion
29722  * @extends Roo.BasicLayoutRegion
29723  * This class represents a region in a layout manager.
29724  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29725  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29726  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29727  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29728  * @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})
29729  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29730  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29731  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29732  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29733  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29734  * @cfg {String} title The title for the region (overrides panel titles)
29735  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29736  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29737  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29738  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29739  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29740  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29741  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29742  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29743  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29744  * @cfg {Boolean} showPin True to show a pin button
29745 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29746 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29747 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29748 * @cfg {Number} width  For East/West panels
29749 * @cfg {Number} height For North/South panels
29750 * @cfg {Boolean} split To show the splitter
29751  */
29752 Roo.LayoutRegion = function(mgr, config, pos){
29753     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29754     var dh = Roo.DomHelper;
29755     /** This region's container element 
29756     * @type Roo.Element */
29757     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29758     /** This region's title element 
29759     * @type Roo.Element */
29760
29761     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29762         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29763         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29764     ]}, true);
29765     this.titleEl.enableDisplayMode();
29766     /** This region's title text element 
29767     * @type HTMLElement */
29768     this.titleTextEl = this.titleEl.dom.firstChild;
29769     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29770     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29771     this.closeBtn.enableDisplayMode();
29772     this.closeBtn.on("click", this.closeClicked, this);
29773     this.closeBtn.hide();
29774
29775     this.createBody(config);
29776     this.visible = true;
29777     this.collapsed = false;
29778
29779     if(config.hideWhenEmpty){
29780         this.hide();
29781         this.on("paneladded", this.validateVisibility, this);
29782         this.on("panelremoved", this.validateVisibility, this);
29783     }
29784     this.applyConfig(config);
29785 };
29786
29787 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29788
29789     createBody : function(){
29790         /** This region's body element 
29791         * @type Roo.Element */
29792         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29793     },
29794
29795     applyConfig : function(c){
29796         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29797             var dh = Roo.DomHelper;
29798             if(c.titlebar !== false){
29799                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29800                 this.collapseBtn.on("click", this.collapse, this);
29801                 this.collapseBtn.enableDisplayMode();
29802
29803                 if(c.showPin === true || this.showPin){
29804                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29805                     this.stickBtn.enableDisplayMode();
29806                     this.stickBtn.on("click", this.expand, this);
29807                     this.stickBtn.hide();
29808                 }
29809             }
29810             /** This region's collapsed element
29811             * @type Roo.Element */
29812             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29813                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29814             ]}, true);
29815             if(c.floatable !== false){
29816                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29817                this.collapsedEl.on("click", this.collapseClick, this);
29818             }
29819
29820             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29821                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29822                    id: "message", unselectable: "on", style:{"float":"left"}});
29823                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29824              }
29825             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29826             this.expandBtn.on("click", this.expand, this);
29827         }
29828         if(this.collapseBtn){
29829             this.collapseBtn.setVisible(c.collapsible == true);
29830         }
29831         this.cmargins = c.cmargins || this.cmargins ||
29832                          (this.position == "west" || this.position == "east" ?
29833                              {top: 0, left: 2, right:2, bottom: 0} :
29834                              {top: 2, left: 0, right:0, bottom: 2});
29835         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29836         this.bottomTabs = c.tabPosition != "top";
29837         this.autoScroll = c.autoScroll || false;
29838         if(this.autoScroll){
29839             this.bodyEl.setStyle("overflow", "auto");
29840         }else{
29841             this.bodyEl.setStyle("overflow", "hidden");
29842         }
29843         //if(c.titlebar !== false){
29844             if((!c.titlebar && !c.title) || c.titlebar === false){
29845                 this.titleEl.hide();
29846             }else{
29847                 this.titleEl.show();
29848                 if(c.title){
29849                     this.titleTextEl.innerHTML = c.title;
29850                 }
29851             }
29852         //}
29853         this.duration = c.duration || .30;
29854         this.slideDuration = c.slideDuration || .45;
29855         this.config = c;
29856         if(c.collapsed){
29857             this.collapse(true);
29858         }
29859         if(c.hidden){
29860             this.hide();
29861         }
29862     },
29863     /**
29864      * Returns true if this region is currently visible.
29865      * @return {Boolean}
29866      */
29867     isVisible : function(){
29868         return this.visible;
29869     },
29870
29871     /**
29872      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29873      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29874      */
29875     setCollapsedTitle : function(title){
29876         title = title || "&#160;";
29877         if(this.collapsedTitleTextEl){
29878             this.collapsedTitleTextEl.innerHTML = title;
29879         }
29880     },
29881
29882     getBox : function(){
29883         var b;
29884         if(!this.collapsed){
29885             b = this.el.getBox(false, true);
29886         }else{
29887             b = this.collapsedEl.getBox(false, true);
29888         }
29889         return b;
29890     },
29891
29892     getMargins : function(){
29893         return this.collapsed ? this.cmargins : this.margins;
29894     },
29895
29896     highlight : function(){
29897         this.el.addClass("x-layout-panel-dragover");
29898     },
29899
29900     unhighlight : function(){
29901         this.el.removeClass("x-layout-panel-dragover");
29902     },
29903
29904     updateBox : function(box){
29905         this.box = box;
29906         if(!this.collapsed){
29907             this.el.dom.style.left = box.x + "px";
29908             this.el.dom.style.top = box.y + "px";
29909             this.updateBody(box.width, box.height);
29910         }else{
29911             this.collapsedEl.dom.style.left = box.x + "px";
29912             this.collapsedEl.dom.style.top = box.y + "px";
29913             this.collapsedEl.setSize(box.width, box.height);
29914         }
29915         if(this.tabs){
29916             this.tabs.autoSizeTabs();
29917         }
29918     },
29919
29920     updateBody : function(w, h){
29921         if(w !== null){
29922             this.el.setWidth(w);
29923             w -= this.el.getBorderWidth("rl");
29924             if(this.config.adjustments){
29925                 w += this.config.adjustments[0];
29926             }
29927         }
29928         if(h !== null){
29929             this.el.setHeight(h);
29930             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29931             h -= this.el.getBorderWidth("tb");
29932             if(this.config.adjustments){
29933                 h += this.config.adjustments[1];
29934             }
29935             this.bodyEl.setHeight(h);
29936             if(this.tabs){
29937                 h = this.tabs.syncHeight(h);
29938             }
29939         }
29940         if(this.panelSize){
29941             w = w !== null ? w : this.panelSize.width;
29942             h = h !== null ? h : this.panelSize.height;
29943         }
29944         if(this.activePanel){
29945             var el = this.activePanel.getEl();
29946             w = w !== null ? w : el.getWidth();
29947             h = h !== null ? h : el.getHeight();
29948             this.panelSize = {width: w, height: h};
29949             this.activePanel.setSize(w, h);
29950         }
29951         if(Roo.isIE && this.tabs){
29952             this.tabs.el.repaint();
29953         }
29954     },
29955
29956     /**
29957      * Returns the container element for this region.
29958      * @return {Roo.Element}
29959      */
29960     getEl : function(){
29961         return this.el;
29962     },
29963
29964     /**
29965      * Hides this region.
29966      */
29967     hide : function(){
29968         if(!this.collapsed){
29969             this.el.dom.style.left = "-2000px";
29970             this.el.hide();
29971         }else{
29972             this.collapsedEl.dom.style.left = "-2000px";
29973             this.collapsedEl.hide();
29974         }
29975         this.visible = false;
29976         this.fireEvent("visibilitychange", this, false);
29977     },
29978
29979     /**
29980      * Shows this region if it was previously hidden.
29981      */
29982     show : function(){
29983         if(!this.collapsed){
29984             this.el.show();
29985         }else{
29986             this.collapsedEl.show();
29987         }
29988         this.visible = true;
29989         this.fireEvent("visibilitychange", this, true);
29990     },
29991
29992     closeClicked : function(){
29993         if(this.activePanel){
29994             this.remove(this.activePanel);
29995         }
29996     },
29997
29998     collapseClick : function(e){
29999         if(this.isSlid){
30000            e.stopPropagation();
30001            this.slideIn();
30002         }else{
30003            e.stopPropagation();
30004            this.slideOut();
30005         }
30006     },
30007
30008     /**
30009      * Collapses this region.
30010      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30011      */
30012     collapse : function(skipAnim){
30013         if(this.collapsed) return;
30014         this.collapsed = true;
30015         if(this.split){
30016             this.split.el.hide();
30017         }
30018         if(this.config.animate && skipAnim !== true){
30019             this.fireEvent("invalidated", this);
30020             this.animateCollapse();
30021         }else{
30022             this.el.setLocation(-20000,-20000);
30023             this.el.hide();
30024             this.collapsedEl.show();
30025             this.fireEvent("collapsed", this);
30026             this.fireEvent("invalidated", this);
30027         }
30028     },
30029
30030     animateCollapse : function(){
30031         // overridden
30032     },
30033
30034     /**
30035      * Expands this region if it was previously collapsed.
30036      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30037      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30038      */
30039     expand : function(e, skipAnim){
30040         if(e) e.stopPropagation();
30041         if(!this.collapsed || this.el.hasActiveFx()) return;
30042         if(this.isSlid){
30043             this.afterSlideIn();
30044             skipAnim = true;
30045         }
30046         this.collapsed = false;
30047         if(this.config.animate && skipAnim !== true){
30048             this.animateExpand();
30049         }else{
30050             this.el.show();
30051             if(this.split){
30052                 this.split.el.show();
30053             }
30054             this.collapsedEl.setLocation(-2000,-2000);
30055             this.collapsedEl.hide();
30056             this.fireEvent("invalidated", this);
30057             this.fireEvent("expanded", this);
30058         }
30059     },
30060
30061     animateExpand : function(){
30062         // overridden
30063     },
30064
30065     initTabs : function(){
30066         this.bodyEl.setStyle("overflow", "hidden");
30067         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30068             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30069             disableTooltips: this.config.disableTabTips
30070         });
30071         if(this.config.hideTabs){
30072             ts.stripWrap.setDisplayed(false);
30073         }
30074         this.tabs = ts;
30075         ts.resizeTabs = this.config.resizeTabs === true;
30076         ts.minTabWidth = this.config.minTabWidth || 40;
30077         ts.maxTabWidth = this.config.maxTabWidth || 250;
30078         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30079         ts.monitorResize = false;
30080         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30081         ts.bodyEl.addClass('x-layout-tabs-body');
30082         this.panels.each(this.initPanelAsTab, this);
30083     },
30084
30085     initPanelAsTab : function(panel){
30086         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30087                     this.config.closeOnTab && panel.isClosable());
30088         if(panel.tabTip !== undefined){
30089             ti.setTooltip(panel.tabTip);
30090         }
30091         ti.on("activate", function(){
30092               this.setActivePanel(panel);
30093         }, this);
30094         if(this.config.closeOnTab){
30095             ti.on("beforeclose", function(t, e){
30096                 e.cancel = true;
30097                 this.remove(panel);
30098             }, this);
30099         }
30100         return ti;
30101     },
30102
30103     updatePanelTitle : function(panel, title){
30104         if(this.activePanel == panel){
30105             this.updateTitle(title);
30106         }
30107         if(this.tabs){
30108             var ti = this.tabs.getTab(panel.getEl().id);
30109             ti.setText(title);
30110             if(panel.tabTip !== undefined){
30111                 ti.setTooltip(panel.tabTip);
30112             }
30113         }
30114     },
30115
30116     updateTitle : function(title){
30117         if(this.titleTextEl && !this.config.title){
30118             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30119         }
30120     },
30121
30122     setActivePanel : function(panel){
30123         panel = this.getPanel(panel);
30124         if(this.activePanel && this.activePanel != panel){
30125             this.activePanel.setActiveState(false);
30126         }
30127         this.activePanel = panel;
30128         panel.setActiveState(true);
30129         if(this.panelSize){
30130             panel.setSize(this.panelSize.width, this.panelSize.height);
30131         }
30132         if(this.closeBtn){
30133             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30134         }
30135         this.updateTitle(panel.getTitle());
30136         if(this.tabs){
30137             this.fireEvent("invalidated", this);
30138         }
30139         this.fireEvent("panelactivated", this, panel);
30140     },
30141
30142     /**
30143      * Shows the specified panel.
30144      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30145      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30146      */
30147     showPanel : function(panel){
30148         if(panel = this.getPanel(panel)){
30149             if(this.tabs){
30150                 var tab = this.tabs.getTab(panel.getEl().id);
30151                 if(tab.isHidden()){
30152                     this.tabs.unhideTab(tab.id);
30153                 }
30154                 tab.activate();
30155             }else{
30156                 this.setActivePanel(panel);
30157             }
30158         }
30159         return panel;
30160     },
30161
30162     /**
30163      * Get the active panel for this region.
30164      * @return {Roo.ContentPanel} The active panel or null
30165      */
30166     getActivePanel : function(){
30167         return this.activePanel;
30168     },
30169
30170     validateVisibility : function(){
30171         if(this.panels.getCount() < 1){
30172             this.updateTitle("&#160;");
30173             this.closeBtn.hide();
30174             this.hide();
30175         }else{
30176             if(!this.isVisible()){
30177                 this.show();
30178             }
30179         }
30180     },
30181
30182     /**
30183      * Adds the passed ContentPanel(s) to this region.
30184      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30185      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30186      */
30187     add : function(panel){
30188         if(arguments.length > 1){
30189             for(var i = 0, len = arguments.length; i < len; i++) {
30190                 this.add(arguments[i]);
30191             }
30192             return null;
30193         }
30194         if(this.hasPanel(panel)){
30195             this.showPanel(panel);
30196             return panel;
30197         }
30198         panel.setRegion(this);
30199         this.panels.add(panel);
30200         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30201             this.bodyEl.dom.appendChild(panel.getEl().dom);
30202             if(panel.background !== true){
30203                 this.setActivePanel(panel);
30204             }
30205             this.fireEvent("paneladded", this, panel);
30206             return panel;
30207         }
30208         if(!this.tabs){
30209             this.initTabs();
30210         }else{
30211             this.initPanelAsTab(panel);
30212         }
30213         if(panel.background !== true){
30214             this.tabs.activate(panel.getEl().id);
30215         }
30216         this.fireEvent("paneladded", this, panel);
30217         return panel;
30218     },
30219
30220     /**
30221      * Hides the tab for the specified panel.
30222      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30223      */
30224     hidePanel : function(panel){
30225         if(this.tabs && (panel = this.getPanel(panel))){
30226             this.tabs.hideTab(panel.getEl().id);
30227         }
30228     },
30229
30230     /**
30231      * Unhides the tab for a previously hidden panel.
30232      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30233      */
30234     unhidePanel : function(panel){
30235         if(this.tabs && (panel = this.getPanel(panel))){
30236             this.tabs.unhideTab(panel.getEl().id);
30237         }
30238     },
30239
30240     clearPanels : function(){
30241         while(this.panels.getCount() > 0){
30242              this.remove(this.panels.first());
30243         }
30244     },
30245
30246     /**
30247      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30249      * @param {Boolean} preservePanel Overrides the config preservePanel option
30250      * @return {Roo.ContentPanel} The panel that was removed
30251      */
30252     remove : function(panel, preservePanel){
30253         panel = this.getPanel(panel);
30254         if(!panel){
30255             return null;
30256         }
30257         var e = {};
30258         this.fireEvent("beforeremove", this, panel, e);
30259         if(e.cancel === true){
30260             return null;
30261         }
30262         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30263         var panelId = panel.getId();
30264         this.panels.removeKey(panelId);
30265         if(preservePanel){
30266             document.body.appendChild(panel.getEl().dom);
30267         }
30268         if(this.tabs){
30269             this.tabs.removeTab(panel.getEl().id);
30270         }else if (!preservePanel){
30271             this.bodyEl.dom.removeChild(panel.getEl().dom);
30272         }
30273         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30274             var p = this.panels.first();
30275             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30276             tempEl.appendChild(p.getEl().dom);
30277             this.bodyEl.update("");
30278             this.bodyEl.dom.appendChild(p.getEl().dom);
30279             tempEl = null;
30280             this.updateTitle(p.getTitle());
30281             this.tabs = null;
30282             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30283             this.setActivePanel(p);
30284         }
30285         panel.setRegion(null);
30286         if(this.activePanel == panel){
30287             this.activePanel = null;
30288         }
30289         if(this.config.autoDestroy !== false && preservePanel !== true){
30290             try{panel.destroy();}catch(e){}
30291         }
30292         this.fireEvent("panelremoved", this, panel);
30293         return panel;
30294     },
30295
30296     /**
30297      * Returns the TabPanel component used by this region
30298      * @return {Roo.TabPanel}
30299      */
30300     getTabs : function(){
30301         return this.tabs;
30302     },
30303
30304     createTool : function(parentEl, className){
30305         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30306             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30307         btn.addClassOnOver("x-layout-tools-button-over");
30308         return btn;
30309     }
30310 });/*
30311  * Based on:
30312  * Ext JS Library 1.1.1
30313  * Copyright(c) 2006-2007, Ext JS, LLC.
30314  *
30315  * Originally Released Under LGPL - original licence link has changed is not relivant.
30316  *
30317  * Fork - LGPL
30318  * <script type="text/javascript">
30319  */
30320  
30321
30322
30323 /**
30324  * @class Roo.SplitLayoutRegion
30325  * @extends Roo.LayoutRegion
30326  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30327  */
30328 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30329     this.cursor = cursor;
30330     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30331 };
30332
30333 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30334     splitTip : "Drag to resize.",
30335     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30336     useSplitTips : false,
30337
30338     applyConfig : function(config){
30339         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30340         if(config.split){
30341             if(!this.split){
30342                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30343                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30344                 /** The SplitBar for this region 
30345                 * @type Roo.SplitBar */
30346                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30347                 this.split.on("moved", this.onSplitMove, this);
30348                 this.split.useShim = config.useShim === true;
30349                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30350                 if(this.useSplitTips){
30351                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30352                 }
30353                 if(config.collapsible){
30354                     this.split.el.on("dblclick", this.collapse,  this);
30355                 }
30356             }
30357             if(typeof config.minSize != "undefined"){
30358                 this.split.minSize = config.minSize;
30359             }
30360             if(typeof config.maxSize != "undefined"){
30361                 this.split.maxSize = config.maxSize;
30362             }
30363             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30364                 this.hideSplitter();
30365             }
30366         }
30367     },
30368
30369     getHMaxSize : function(){
30370          var cmax = this.config.maxSize || 10000;
30371          var center = this.mgr.getRegion("center");
30372          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30373     },
30374
30375     getVMaxSize : function(){
30376          var cmax = this.config.maxSize || 10000;
30377          var center = this.mgr.getRegion("center");
30378          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30379     },
30380
30381     onSplitMove : function(split, newSize){
30382         this.fireEvent("resized", this, newSize);
30383     },
30384     
30385     /** 
30386      * Returns the {@link Roo.SplitBar} for this region.
30387      * @return {Roo.SplitBar}
30388      */
30389     getSplitBar : function(){
30390         return this.split;
30391     },
30392     
30393     hide : function(){
30394         this.hideSplitter();
30395         Roo.SplitLayoutRegion.superclass.hide.call(this);
30396     },
30397
30398     hideSplitter : function(){
30399         if(this.split){
30400             this.split.el.setLocation(-2000,-2000);
30401             this.split.el.hide();
30402         }
30403     },
30404
30405     show : function(){
30406         if(this.split){
30407             this.split.el.show();
30408         }
30409         Roo.SplitLayoutRegion.superclass.show.call(this);
30410     },
30411     
30412     beforeSlide: function(){
30413         if(Roo.isGecko){// firefox overflow auto bug workaround
30414             this.bodyEl.clip();
30415             if(this.tabs) this.tabs.bodyEl.clip();
30416             if(this.activePanel){
30417                 this.activePanel.getEl().clip();
30418                 
30419                 if(this.activePanel.beforeSlide){
30420                     this.activePanel.beforeSlide();
30421                 }
30422             }
30423         }
30424     },
30425     
30426     afterSlide : function(){
30427         if(Roo.isGecko){// firefox overflow auto bug workaround
30428             this.bodyEl.unclip();
30429             if(this.tabs) this.tabs.bodyEl.unclip();
30430             if(this.activePanel){
30431                 this.activePanel.getEl().unclip();
30432                 if(this.activePanel.afterSlide){
30433                     this.activePanel.afterSlide();
30434                 }
30435             }
30436         }
30437     },
30438
30439     initAutoHide : function(){
30440         if(this.autoHide !== false){
30441             if(!this.autoHideHd){
30442                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30443                 this.autoHideHd = {
30444                     "mouseout": function(e){
30445                         if(!e.within(this.el, true)){
30446                             st.delay(500);
30447                         }
30448                     },
30449                     "mouseover" : function(e){
30450                         st.cancel();
30451                     },
30452                     scope : this
30453                 };
30454             }
30455             this.el.on(this.autoHideHd);
30456         }
30457     },
30458
30459     clearAutoHide : function(){
30460         if(this.autoHide !== false){
30461             this.el.un("mouseout", this.autoHideHd.mouseout);
30462             this.el.un("mouseover", this.autoHideHd.mouseover);
30463         }
30464     },
30465
30466     clearMonitor : function(){
30467         Roo.get(document).un("click", this.slideInIf, this);
30468     },
30469
30470     // these names are backwards but not changed for compat
30471     slideOut : function(){
30472         if(this.isSlid || this.el.hasActiveFx()){
30473             return;
30474         }
30475         this.isSlid = true;
30476         if(this.collapseBtn){
30477             this.collapseBtn.hide();
30478         }
30479         this.closeBtnState = this.closeBtn.getStyle('display');
30480         this.closeBtn.hide();
30481         if(this.stickBtn){
30482             this.stickBtn.show();
30483         }
30484         this.el.show();
30485         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30486         this.beforeSlide();
30487         this.el.setStyle("z-index", 10001);
30488         this.el.slideIn(this.getSlideAnchor(), {
30489             callback: function(){
30490                 this.afterSlide();
30491                 this.initAutoHide();
30492                 Roo.get(document).on("click", this.slideInIf, this);
30493                 this.fireEvent("slideshow", this);
30494             },
30495             scope: this,
30496             block: true
30497         });
30498     },
30499
30500     afterSlideIn : function(){
30501         this.clearAutoHide();
30502         this.isSlid = false;
30503         this.clearMonitor();
30504         this.el.setStyle("z-index", "");
30505         if(this.collapseBtn){
30506             this.collapseBtn.show();
30507         }
30508         this.closeBtn.setStyle('display', this.closeBtnState);
30509         if(this.stickBtn){
30510             this.stickBtn.hide();
30511         }
30512         this.fireEvent("slidehide", this);
30513     },
30514
30515     slideIn : function(cb){
30516         if(!this.isSlid || this.el.hasActiveFx()){
30517             Roo.callback(cb);
30518             return;
30519         }
30520         this.isSlid = false;
30521         this.beforeSlide();
30522         this.el.slideOut(this.getSlideAnchor(), {
30523             callback: function(){
30524                 this.el.setLeftTop(-10000, -10000);
30525                 this.afterSlide();
30526                 this.afterSlideIn();
30527                 Roo.callback(cb);
30528             },
30529             scope: this,
30530             block: true
30531         });
30532     },
30533     
30534     slideInIf : function(e){
30535         if(!e.within(this.el)){
30536             this.slideIn();
30537         }
30538     },
30539
30540     animateCollapse : function(){
30541         this.beforeSlide();
30542         this.el.setStyle("z-index", 20000);
30543         var anchor = this.getSlideAnchor();
30544         this.el.slideOut(anchor, {
30545             callback : function(){
30546                 this.el.setStyle("z-index", "");
30547                 this.collapsedEl.slideIn(anchor, {duration:.3});
30548                 this.afterSlide();
30549                 this.el.setLocation(-10000,-10000);
30550                 this.el.hide();
30551                 this.fireEvent("collapsed", this);
30552             },
30553             scope: this,
30554             block: true
30555         });
30556     },
30557
30558     animateExpand : function(){
30559         this.beforeSlide();
30560         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30561         this.el.setStyle("z-index", 20000);
30562         this.collapsedEl.hide({
30563             duration:.1
30564         });
30565         this.el.slideIn(this.getSlideAnchor(), {
30566             callback : function(){
30567                 this.el.setStyle("z-index", "");
30568                 this.afterSlide();
30569                 if(this.split){
30570                     this.split.el.show();
30571                 }
30572                 this.fireEvent("invalidated", this);
30573                 this.fireEvent("expanded", this);
30574             },
30575             scope: this,
30576             block: true
30577         });
30578     },
30579
30580     anchors : {
30581         "west" : "left",
30582         "east" : "right",
30583         "north" : "top",
30584         "south" : "bottom"
30585     },
30586
30587     sanchors : {
30588         "west" : "l",
30589         "east" : "r",
30590         "north" : "t",
30591         "south" : "b"
30592     },
30593
30594     canchors : {
30595         "west" : "tl-tr",
30596         "east" : "tr-tl",
30597         "north" : "tl-bl",
30598         "south" : "bl-tl"
30599     },
30600
30601     getAnchor : function(){
30602         return this.anchors[this.position];
30603     },
30604
30605     getCollapseAnchor : function(){
30606         return this.canchors[this.position];
30607     },
30608
30609     getSlideAnchor : function(){
30610         return this.sanchors[this.position];
30611     },
30612
30613     getAlignAdj : function(){
30614         var cm = this.cmargins;
30615         switch(this.position){
30616             case "west":
30617                 return [0, 0];
30618             break;
30619             case "east":
30620                 return [0, 0];
30621             break;
30622             case "north":
30623                 return [0, 0];
30624             break;
30625             case "south":
30626                 return [0, 0];
30627             break;
30628         }
30629     },
30630
30631     getExpandAdj : function(){
30632         var c = this.collapsedEl, cm = this.cmargins;
30633         switch(this.position){
30634             case "west":
30635                 return [-(cm.right+c.getWidth()+cm.left), 0];
30636             break;
30637             case "east":
30638                 return [cm.right+c.getWidth()+cm.left, 0];
30639             break;
30640             case "north":
30641                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30642             break;
30643             case "south":
30644                 return [0, cm.top+cm.bottom+c.getHeight()];
30645             break;
30646         }
30647     }
30648 });/*
30649  * Based on:
30650  * Ext JS Library 1.1.1
30651  * Copyright(c) 2006-2007, Ext JS, LLC.
30652  *
30653  * Originally Released Under LGPL - original licence link has changed is not relivant.
30654  *
30655  * Fork - LGPL
30656  * <script type="text/javascript">
30657  */
30658 /*
30659  * These classes are private internal classes
30660  */
30661 Roo.CenterLayoutRegion = function(mgr, config){
30662     Roo.LayoutRegion.call(this, mgr, config, "center");
30663     this.visible = true;
30664     this.minWidth = config.minWidth || 20;
30665     this.minHeight = config.minHeight || 20;
30666 };
30667
30668 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30669     hide : function(){
30670         // center panel can't be hidden
30671     },
30672     
30673     show : function(){
30674         // center panel can't be hidden
30675     },
30676     
30677     getMinWidth: function(){
30678         return this.minWidth;
30679     },
30680     
30681     getMinHeight: function(){
30682         return this.minHeight;
30683     }
30684 });
30685
30686
30687 Roo.NorthLayoutRegion = function(mgr, config){
30688     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30689     if(this.split){
30690         this.split.placement = Roo.SplitBar.TOP;
30691         this.split.orientation = Roo.SplitBar.VERTICAL;
30692         this.split.el.addClass("x-layout-split-v");
30693     }
30694     var size = config.initialSize || config.height;
30695     if(typeof size != "undefined"){
30696         this.el.setHeight(size);
30697     }
30698 };
30699 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30700     orientation: Roo.SplitBar.VERTICAL,
30701     getBox : function(){
30702         if(this.collapsed){
30703             return this.collapsedEl.getBox();
30704         }
30705         var box = this.el.getBox();
30706         if(this.split){
30707             box.height += this.split.el.getHeight();
30708         }
30709         return box;
30710     },
30711     
30712     updateBox : function(box){
30713         if(this.split && !this.collapsed){
30714             box.height -= this.split.el.getHeight();
30715             this.split.el.setLeft(box.x);
30716             this.split.el.setTop(box.y+box.height);
30717             this.split.el.setWidth(box.width);
30718         }
30719         if(this.collapsed){
30720             this.updateBody(box.width, null);
30721         }
30722         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30723     }
30724 });
30725
30726 Roo.SouthLayoutRegion = function(mgr, config){
30727     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30728     if(this.split){
30729         this.split.placement = Roo.SplitBar.BOTTOM;
30730         this.split.orientation = Roo.SplitBar.VERTICAL;
30731         this.split.el.addClass("x-layout-split-v");
30732     }
30733     var size = config.initialSize || config.height;
30734     if(typeof size != "undefined"){
30735         this.el.setHeight(size);
30736     }
30737 };
30738 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30739     orientation: Roo.SplitBar.VERTICAL,
30740     getBox : function(){
30741         if(this.collapsed){
30742             return this.collapsedEl.getBox();
30743         }
30744         var box = this.el.getBox();
30745         if(this.split){
30746             var sh = this.split.el.getHeight();
30747             box.height += sh;
30748             box.y -= sh;
30749         }
30750         return box;
30751     },
30752     
30753     updateBox : function(box){
30754         if(this.split && !this.collapsed){
30755             var sh = this.split.el.getHeight();
30756             box.height -= sh;
30757             box.y += sh;
30758             this.split.el.setLeft(box.x);
30759             this.split.el.setTop(box.y-sh);
30760             this.split.el.setWidth(box.width);
30761         }
30762         if(this.collapsed){
30763             this.updateBody(box.width, null);
30764         }
30765         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30766     }
30767 });
30768
30769 Roo.EastLayoutRegion = function(mgr, config){
30770     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30771     if(this.split){
30772         this.split.placement = Roo.SplitBar.RIGHT;
30773         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30774         this.split.el.addClass("x-layout-split-h");
30775     }
30776     var size = config.initialSize || config.width;
30777     if(typeof size != "undefined"){
30778         this.el.setWidth(size);
30779     }
30780 };
30781 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30782     orientation: Roo.SplitBar.HORIZONTAL,
30783     getBox : function(){
30784         if(this.collapsed){
30785             return this.collapsedEl.getBox();
30786         }
30787         var box = this.el.getBox();
30788         if(this.split){
30789             var sw = this.split.el.getWidth();
30790             box.width += sw;
30791             box.x -= sw;
30792         }
30793         return box;
30794     },
30795
30796     updateBox : function(box){
30797         if(this.split && !this.collapsed){
30798             var sw = this.split.el.getWidth();
30799             box.width -= sw;
30800             this.split.el.setLeft(box.x);
30801             this.split.el.setTop(box.y);
30802             this.split.el.setHeight(box.height);
30803             box.x += sw;
30804         }
30805         if(this.collapsed){
30806             this.updateBody(null, box.height);
30807         }
30808         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30809     }
30810 });
30811
30812 Roo.WestLayoutRegion = function(mgr, config){
30813     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30814     if(this.split){
30815         this.split.placement = Roo.SplitBar.LEFT;
30816         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30817         this.split.el.addClass("x-layout-split-h");
30818     }
30819     var size = config.initialSize || config.width;
30820     if(typeof size != "undefined"){
30821         this.el.setWidth(size);
30822     }
30823 };
30824 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30825     orientation: Roo.SplitBar.HORIZONTAL,
30826     getBox : function(){
30827         if(this.collapsed){
30828             return this.collapsedEl.getBox();
30829         }
30830         var box = this.el.getBox();
30831         if(this.split){
30832             box.width += this.split.el.getWidth();
30833         }
30834         return box;
30835     },
30836     
30837     updateBox : function(box){
30838         if(this.split && !this.collapsed){
30839             var sw = this.split.el.getWidth();
30840             box.width -= sw;
30841             this.split.el.setLeft(box.x+box.width);
30842             this.split.el.setTop(box.y);
30843             this.split.el.setHeight(box.height);
30844         }
30845         if(this.collapsed){
30846             this.updateBody(null, box.height);
30847         }
30848         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30849     }
30850 });
30851 /*
30852  * Based on:
30853  * Ext JS Library 1.1.1
30854  * Copyright(c) 2006-2007, Ext JS, LLC.
30855  *
30856  * Originally Released Under LGPL - original licence link has changed is not relivant.
30857  *
30858  * Fork - LGPL
30859  * <script type="text/javascript">
30860  */
30861  
30862  
30863 /*
30864  * Private internal class for reading and applying state
30865  */
30866 Roo.LayoutStateManager = function(layout){
30867      // default empty state
30868      this.state = {
30869         north: {},
30870         south: {},
30871         east: {},
30872         west: {}       
30873     };
30874 };
30875
30876 Roo.LayoutStateManager.prototype = {
30877     init : function(layout, provider){
30878         this.provider = provider;
30879         var state = provider.get(layout.id+"-layout-state");
30880         if(state){
30881             var wasUpdating = layout.isUpdating();
30882             if(!wasUpdating){
30883                 layout.beginUpdate();
30884             }
30885             for(var key in state){
30886                 if(typeof state[key] != "function"){
30887                     var rstate = state[key];
30888                     var r = layout.getRegion(key);
30889                     if(r && rstate){
30890                         if(rstate.size){
30891                             r.resizeTo(rstate.size);
30892                         }
30893                         if(rstate.collapsed == true){
30894                             r.collapse(true);
30895                         }else{
30896                             r.expand(null, true);
30897                         }
30898                     }
30899                 }
30900             }
30901             if(!wasUpdating){
30902                 layout.endUpdate();
30903             }
30904             this.state = state; 
30905         }
30906         this.layout = layout;
30907         layout.on("regionresized", this.onRegionResized, this);
30908         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30909         layout.on("regionexpanded", this.onRegionExpanded, this);
30910     },
30911     
30912     storeState : function(){
30913         this.provider.set(this.layout.id+"-layout-state", this.state);
30914     },
30915     
30916     onRegionResized : function(region, newSize){
30917         this.state[region.getPosition()].size = newSize;
30918         this.storeState();
30919     },
30920     
30921     onRegionCollapsed : function(region){
30922         this.state[region.getPosition()].collapsed = true;
30923         this.storeState();
30924     },
30925     
30926     onRegionExpanded : function(region){
30927         this.state[region.getPosition()].collapsed = false;
30928         this.storeState();
30929     }
30930 };/*
30931  * Based on:
30932  * Ext JS Library 1.1.1
30933  * Copyright(c) 2006-2007, Ext JS, LLC.
30934  *
30935  * Originally Released Under LGPL - original licence link has changed is not relivant.
30936  *
30937  * Fork - LGPL
30938  * <script type="text/javascript">
30939  */
30940 /**
30941  * @class Roo.ContentPanel
30942  * @extends Roo.util.Observable
30943  * A basic ContentPanel element.
30944  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30945  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30946  * @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
30947  * @cfg {Boolean} closable True if the panel can be closed/removed
30948  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30949  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30950  * @cfg {Toolbar} toolbar A toolbar for this panel
30951  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30952  * @cfg {String} title The title for this panel
30953  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30954  * @cfg {String} url Calls {@link #setUrl} with this value
30955  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30956  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30957  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30958  * @constructor
30959  * Create a new ContentPanel.
30960  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30961  * @param {String/Object} config A string to set only the title or a config object
30962  * @param {String} content (optional) Set the HTML content for this panel
30963  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30964  */
30965 Roo.ContentPanel = function(el, config, content){
30966     
30967      
30968     /*
30969     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30970         config = el;
30971         el = Roo.id();
30972     }
30973     if (config && config.parentLayout) { 
30974         el = config.parentLayout.el.createChild(); 
30975     }
30976     */
30977     if(el.autoCreate){ // xtype is available if this is called from factory
30978         config = el;
30979         el = Roo.id();
30980     }
30981     this.el = Roo.get(el);
30982     if(!this.el && config && config.autoCreate){
30983         if(typeof config.autoCreate == "object"){
30984             if(!config.autoCreate.id){
30985                 config.autoCreate.id = config.id||el;
30986             }
30987             this.el = Roo.DomHelper.append(document.body,
30988                         config.autoCreate, true);
30989         }else{
30990             this.el = Roo.DomHelper.append(document.body,
30991                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30992         }
30993     }
30994     this.closable = false;
30995     this.loaded = false;
30996     this.active = false;
30997     if(typeof config == "string"){
30998         this.title = config;
30999     }else{
31000         Roo.apply(this, config);
31001     }
31002     
31003     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31004         this.wrapEl = this.el.wrap();    
31005         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31006         
31007     }
31008     
31009     
31010     
31011     if(this.resizeEl){
31012         this.resizeEl = Roo.get(this.resizeEl, true);
31013     }else{
31014         this.resizeEl = this.el;
31015     }
31016     this.addEvents({
31017         /**
31018          * @event activate
31019          * Fires when this panel is activated. 
31020          * @param {Roo.ContentPanel} this
31021          */
31022         "activate" : true,
31023         /**
31024          * @event deactivate
31025          * Fires when this panel is activated. 
31026          * @param {Roo.ContentPanel} this
31027          */
31028         "deactivate" : true,
31029
31030         /**
31031          * @event resize
31032          * Fires when this panel is resized if fitToFrame is true.
31033          * @param {Roo.ContentPanel} this
31034          * @param {Number} width The width after any component adjustments
31035          * @param {Number} height The height after any component adjustments
31036          */
31037         "resize" : true
31038     });
31039     if(this.autoScroll){
31040         this.resizeEl.setStyle("overflow", "auto");
31041     } else {
31042         // fix randome scrolling
31043         this.el.on('scroll', function() {
31044             this.scrollTo('top',0); 
31045         });
31046     }
31047     content = content || this.content;
31048     if(content){
31049         this.setContent(content);
31050     }
31051     if(config && config.url){
31052         this.setUrl(this.url, this.params, this.loadOnce);
31053     }
31054     
31055     
31056     
31057     Roo.ContentPanel.superclass.constructor.call(this);
31058 };
31059
31060 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31061     tabTip:'',
31062     setRegion : function(region){
31063         this.region = region;
31064         if(region){
31065            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31066         }else{
31067            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31068         } 
31069     },
31070     
31071     /**
31072      * Returns the toolbar for this Panel if one was configured. 
31073      * @return {Roo.Toolbar} 
31074      */
31075     getToolbar : function(){
31076         return this.toolbar;
31077     },
31078     
31079     setActiveState : function(active){
31080         this.active = active;
31081         if(!active){
31082             this.fireEvent("deactivate", this);
31083         }else{
31084             this.fireEvent("activate", this);
31085         }
31086     },
31087     /**
31088      * Updates this panel's element
31089      * @param {String} content The new content
31090      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31091     */
31092     setContent : function(content, loadScripts){
31093         this.el.update(content, loadScripts);
31094     },
31095
31096     ignoreResize : function(w, h){
31097         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31098             return true;
31099         }else{
31100             this.lastSize = {width: w, height: h};
31101             return false;
31102         }
31103     },
31104     /**
31105      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31106      * @return {Roo.UpdateManager} The UpdateManager
31107      */
31108     getUpdateManager : function(){
31109         return this.el.getUpdateManager();
31110     },
31111      /**
31112      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31113      * @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:
31114 <pre><code>
31115 panel.load({
31116     url: "your-url.php",
31117     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31118     callback: yourFunction,
31119     scope: yourObject, //(optional scope)
31120     discardUrl: false,
31121     nocache: false,
31122     text: "Loading...",
31123     timeout: 30,
31124     scripts: false
31125 });
31126 </code></pre>
31127      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31128      * 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.
31129      * @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}
31130      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31131      * @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.
31132      * @return {Roo.ContentPanel} this
31133      */
31134     load : function(){
31135         var um = this.el.getUpdateManager();
31136         um.update.apply(um, arguments);
31137         return this;
31138     },
31139
31140
31141     /**
31142      * 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.
31143      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31144      * @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)
31145      * @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)
31146      * @return {Roo.UpdateManager} The UpdateManager
31147      */
31148     setUrl : function(url, params, loadOnce){
31149         if(this.refreshDelegate){
31150             this.removeListener("activate", this.refreshDelegate);
31151         }
31152         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31153         this.on("activate", this.refreshDelegate);
31154         return this.el.getUpdateManager();
31155     },
31156     
31157     _handleRefresh : function(url, params, loadOnce){
31158         if(!loadOnce || !this.loaded){
31159             var updater = this.el.getUpdateManager();
31160             updater.update(url, params, this._setLoaded.createDelegate(this));
31161         }
31162     },
31163     
31164     _setLoaded : function(){
31165         this.loaded = true;
31166     }, 
31167     
31168     /**
31169      * Returns this panel's id
31170      * @return {String} 
31171      */
31172     getId : function(){
31173         return this.el.id;
31174     },
31175     
31176     /** 
31177      * Returns this panel's element - used by regiosn to add.
31178      * @return {Roo.Element} 
31179      */
31180     getEl : function(){
31181         return this.wrapEl || this.el;
31182     },
31183     
31184     adjustForComponents : function(width, height){
31185         if(this.resizeEl != this.el){
31186             width -= this.el.getFrameWidth('lr');
31187             height -= this.el.getFrameWidth('tb');
31188         }
31189         if(this.toolbar){
31190             var te = this.toolbar.getEl();
31191             height -= te.getHeight();
31192             te.setWidth(width);
31193         }
31194         if(this.adjustments){
31195             width += this.adjustments[0];
31196             height += this.adjustments[1];
31197         }
31198         return {"width": width, "height": height};
31199     },
31200     
31201     setSize : function(width, height){
31202         if(this.fitToFrame && !this.ignoreResize(width, height)){
31203             if(this.fitContainer && this.resizeEl != this.el){
31204                 this.el.setSize(width, height);
31205             }
31206             var size = this.adjustForComponents(width, height);
31207             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31208             this.fireEvent('resize', this, size.width, size.height);
31209         }
31210     },
31211     
31212     /**
31213      * Returns this panel's title
31214      * @return {String} 
31215      */
31216     getTitle : function(){
31217         return this.title;
31218     },
31219     
31220     /**
31221      * Set this panel's title
31222      * @param {String} title
31223      */
31224     setTitle : function(title){
31225         this.title = title;
31226         if(this.region){
31227             this.region.updatePanelTitle(this, title);
31228         }
31229     },
31230     
31231     /**
31232      * Returns true is this panel was configured to be closable
31233      * @return {Boolean} 
31234      */
31235     isClosable : function(){
31236         return this.closable;
31237     },
31238     
31239     beforeSlide : function(){
31240         this.el.clip();
31241         this.resizeEl.clip();
31242     },
31243     
31244     afterSlide : function(){
31245         this.el.unclip();
31246         this.resizeEl.unclip();
31247     },
31248     
31249     /**
31250      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31251      *   Will fail silently if the {@link #setUrl} method has not been called.
31252      *   This does not activate the panel, just updates its content.
31253      */
31254     refresh : function(){
31255         if(this.refreshDelegate){
31256            this.loaded = false;
31257            this.refreshDelegate();
31258         }
31259     },
31260     
31261     /**
31262      * Destroys this panel
31263      */
31264     destroy : function(){
31265         this.el.removeAllListeners();
31266         var tempEl = document.createElement("span");
31267         tempEl.appendChild(this.el.dom);
31268         tempEl.innerHTML = "";
31269         this.el.remove();
31270         this.el = null;
31271     },
31272     
31273       /**
31274      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31275      * <pre><code>
31276
31277 layout.addxtype({
31278        xtype : 'Form',
31279        items: [ .... ]
31280    }
31281 );
31282
31283 </code></pre>
31284      * @param {Object} cfg Xtype definition of item to add.
31285      */
31286     
31287     addxtype : function(cfg) {
31288         // add form..
31289         if (cfg.xtype.match(/^Form$/)) {
31290             var el = this.el.createChild();
31291
31292             this.form = new  Roo.form.Form(cfg);
31293             
31294             
31295             if ( this.form.allItems.length) this.form.render(el.dom);
31296             return this.form;
31297         }
31298         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31299             // views..
31300             cfg.el = this.el.appendChild(document.createElement("div"));
31301             // factory?
31302             var ret = new Roo[cfg.xtype](cfg);
31303             ret.render(false, ''); // render blank..
31304             return ret;
31305             
31306         }
31307         return false;
31308         
31309     }
31310 });
31311
31312 /**
31313  * @class Roo.GridPanel
31314  * @extends Roo.ContentPanel
31315  * @constructor
31316  * Create a new GridPanel.
31317  * @param {Roo.grid.Grid} grid The grid for this panel
31318  * @param {String/Object} config A string to set only the panel's title, or a config object
31319  */
31320 Roo.GridPanel = function(grid, config){
31321     
31322   
31323     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31324         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31325         
31326     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31327     
31328     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31329     
31330     if(this.toolbar){
31331         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31332     }
31333     // xtype created footer. - not sure if will work as we normally have to render first..
31334     if (this.footer && !this.footer.el && this.footer.xtype) {
31335         
31336         this.footer.container = this.grid.getView().getFooterPanel(true);
31337         this.footer.dataSource = this.grid.dataSource;
31338         this.footer = Roo.factory(this.footer, Roo);
31339         
31340     }
31341     
31342     grid.monitorWindowResize = false; // turn off autosizing
31343     grid.autoHeight = false;
31344     grid.autoWidth = false;
31345     this.grid = grid;
31346     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31347 };
31348
31349 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31350     getId : function(){
31351         return this.grid.id;
31352     },
31353     
31354     /**
31355      * Returns the grid for this panel
31356      * @return {Roo.grid.Grid} 
31357      */
31358     getGrid : function(){
31359         return this.grid;    
31360     },
31361     
31362     setSize : function(width, height){
31363         if(!this.ignoreResize(width, height)){
31364             var grid = this.grid;
31365             var size = this.adjustForComponents(width, height);
31366             grid.getGridEl().setSize(size.width, size.height);
31367             grid.autoSize();
31368         }
31369     },
31370     
31371     beforeSlide : function(){
31372         this.grid.getView().scroller.clip();
31373     },
31374     
31375     afterSlide : function(){
31376         this.grid.getView().scroller.unclip();
31377     },
31378     
31379     destroy : function(){
31380         this.grid.destroy();
31381         delete this.grid;
31382         Roo.GridPanel.superclass.destroy.call(this); 
31383     }
31384 });
31385
31386
31387 /**
31388  * @class Roo.NestedLayoutPanel
31389  * @extends Roo.ContentPanel
31390  * @constructor
31391  * Create a new NestedLayoutPanel.
31392  * 
31393  * 
31394  * @param {Roo.BorderLayout} layout The layout for this panel
31395  * @param {String/Object} config A string to set only the title or a config object
31396  */
31397 Roo.NestedLayoutPanel = function(layout, config)
31398 {
31399     // construct with only one argument..
31400     /* FIXME - implement nicer consturctors
31401     if (layout.layout) {
31402         config = layout;
31403         layout = config.layout;
31404         delete config.layout;
31405     }
31406     if (layout.xtype && !layout.getEl) {
31407         // then layout needs constructing..
31408         layout = Roo.factory(layout, Roo);
31409     }
31410     */
31411     
31412     
31413     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31414     
31415     layout.monitorWindowResize = false; // turn off autosizing
31416     this.layout = layout;
31417     this.layout.getEl().addClass("x-layout-nested-layout");
31418     
31419     
31420     
31421     
31422 };
31423
31424 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31425
31426     setSize : function(width, height){
31427         if(!this.ignoreResize(width, height)){
31428             var size = this.adjustForComponents(width, height);
31429             var el = this.layout.getEl();
31430             el.setSize(size.width, size.height);
31431             var touch = el.dom.offsetWidth;
31432             this.layout.layout();
31433             // ie requires a double layout on the first pass
31434             if(Roo.isIE && !this.initialized){
31435                 this.initialized = true;
31436                 this.layout.layout();
31437             }
31438         }
31439     },
31440     
31441     // activate all subpanels if not currently active..
31442     
31443     setActiveState : function(active){
31444         this.active = active;
31445         if(!active){
31446             this.fireEvent("deactivate", this);
31447             return;
31448         }
31449         
31450         this.fireEvent("activate", this);
31451         // not sure if this should happen before or after..
31452         if (!this.layout) {
31453             return; // should not happen..
31454         }
31455         var reg = false;
31456         for (var r in this.layout.regions) {
31457             reg = this.layout.getRegion(r);
31458             if (reg.getActivePanel()) {
31459                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31460                 reg.setActivePanel(reg.getActivePanel());
31461                 continue;
31462             }
31463             if (!reg.panels.length) {
31464                 continue;
31465             }
31466             reg.showPanel(reg.getPanel(0));
31467         }
31468         
31469         
31470         
31471         
31472     },
31473     
31474     /**
31475      * Returns the nested BorderLayout for this panel
31476      * @return {Roo.BorderLayout} 
31477      */
31478     getLayout : function(){
31479         return this.layout;
31480     },
31481     
31482      /**
31483      * Adds a xtype elements to the layout of the nested panel
31484      * <pre><code>
31485
31486 panel.addxtype({
31487        xtype : 'ContentPanel',
31488        region: 'west',
31489        items: [ .... ]
31490    }
31491 );
31492
31493 panel.addxtype({
31494         xtype : 'NestedLayoutPanel',
31495         region: 'west',
31496         layout: {
31497            center: { },
31498            west: { }   
31499         },
31500         items : [ ... list of content panels or nested layout panels.. ]
31501    }
31502 );
31503 </code></pre>
31504      * @param {Object} cfg Xtype definition of item to add.
31505      */
31506     addxtype : function(cfg) {
31507         return this.layout.addxtype(cfg);
31508     
31509     }
31510 });
31511
31512 Roo.ScrollPanel = function(el, config, content){
31513     config = config || {};
31514     config.fitToFrame = true;
31515     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31516     
31517     this.el.dom.style.overflow = "hidden";
31518     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31519     this.el.removeClass("x-layout-inactive-content");
31520     this.el.on("mousewheel", this.onWheel, this);
31521
31522     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31523     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31524     up.unselectable(); down.unselectable();
31525     up.on("click", this.scrollUp, this);
31526     down.on("click", this.scrollDown, this);
31527     up.addClassOnOver("x-scroller-btn-over");
31528     down.addClassOnOver("x-scroller-btn-over");
31529     up.addClassOnClick("x-scroller-btn-click");
31530     down.addClassOnClick("x-scroller-btn-click");
31531     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31532
31533     this.resizeEl = this.el;
31534     this.el = wrap; this.up = up; this.down = down;
31535 };
31536
31537 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31538     increment : 100,
31539     wheelIncrement : 5,
31540     scrollUp : function(){
31541         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31542     },
31543
31544     scrollDown : function(){
31545         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31546     },
31547
31548     afterScroll : function(){
31549         var el = this.resizeEl;
31550         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31551         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31552         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31553     },
31554
31555     setSize : function(){
31556         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31557         this.afterScroll();
31558     },
31559
31560     onWheel : function(e){
31561         var d = e.getWheelDelta();
31562         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31563         this.afterScroll();
31564         e.stopEvent();
31565     },
31566
31567     setContent : function(content, loadScripts){
31568         this.resizeEl.update(content, loadScripts);
31569     }
31570
31571 });
31572
31573
31574
31575
31576
31577
31578
31579
31580
31581 /**
31582  * @class Roo.TreePanel
31583  * @extends Roo.ContentPanel
31584  * @constructor
31585  * Create a new TreePanel. - defaults to fit/scoll contents.
31586  * @param {String/Object} config A string to set only the panel's title, or a config object
31587  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31588  */
31589 Roo.TreePanel = function(config){
31590     var el = config.el;
31591     var tree = config.tree;
31592     delete config.tree; 
31593     delete config.el; // hopefull!
31594     
31595     // wrapper for IE7 strict & safari scroll issue
31596     
31597     var treeEl = el.createChild();
31598     config.resizeEl = treeEl;
31599     
31600     
31601     
31602     Roo.TreePanel.superclass.constructor.call(this, el, config);
31603  
31604  
31605     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31606     //console.log(tree);
31607     this.on('activate', function()
31608     {
31609         if (this.tree.rendered) {
31610             return;
31611         }
31612         //console.log('render tree');
31613         this.tree.render();
31614     });
31615     
31616     this.on('resize',  function (cp, w, h) {
31617             this.tree.innerCt.setWidth(w);
31618             this.tree.innerCt.setHeight(h);
31619             this.tree.innerCt.setStyle('overflow-y', 'auto');
31620     });
31621
31622         
31623     
31624 };
31625
31626 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31627     fitToFrame : true,
31628     autoScroll : true
31629 });
31630
31631
31632
31633
31634
31635
31636
31637
31638
31639
31640
31641 /*
31642  * Based on:
31643  * Ext JS Library 1.1.1
31644  * Copyright(c) 2006-2007, Ext JS, LLC.
31645  *
31646  * Originally Released Under LGPL - original licence link has changed is not relivant.
31647  *
31648  * Fork - LGPL
31649  * <script type="text/javascript">
31650  */
31651  
31652
31653 /**
31654  * @class Roo.ReaderLayout
31655  * @extends Roo.BorderLayout
31656  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31657  * center region containing two nested regions (a top one for a list view and one for item preview below),
31658  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31659  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31660  * expedites the setup of the overall layout and regions for this common application style.
31661  * Example:
31662  <pre><code>
31663 var reader = new Roo.ReaderLayout();
31664 var CP = Roo.ContentPanel;  // shortcut for adding
31665
31666 reader.beginUpdate();
31667 reader.add("north", new CP("north", "North"));
31668 reader.add("west", new CP("west", {title: "West"}));
31669 reader.add("east", new CP("east", {title: "East"}));
31670
31671 reader.regions.listView.add(new CP("listView", "List"));
31672 reader.regions.preview.add(new CP("preview", "Preview"));
31673 reader.endUpdate();
31674 </code></pre>
31675 * @constructor
31676 * Create a new ReaderLayout
31677 * @param {Object} config Configuration options
31678 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31679 * document.body if omitted)
31680 */
31681 Roo.ReaderLayout = function(config, renderTo){
31682     var c = config || {size:{}};
31683     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31684         north: c.north !== false ? Roo.apply({
31685             split:false,
31686             initialSize: 32,
31687             titlebar: false
31688         }, c.north) : false,
31689         west: c.west !== false ? Roo.apply({
31690             split:true,
31691             initialSize: 200,
31692             minSize: 175,
31693             maxSize: 400,
31694             titlebar: true,
31695             collapsible: true,
31696             animate: true,
31697             margins:{left:5,right:0,bottom:5,top:5},
31698             cmargins:{left:5,right:5,bottom:5,top:5}
31699         }, c.west) : false,
31700         east: c.east !== false ? Roo.apply({
31701             split:true,
31702             initialSize: 200,
31703             minSize: 175,
31704             maxSize: 400,
31705             titlebar: true,
31706             collapsible: true,
31707             animate: true,
31708             margins:{left:0,right:5,bottom:5,top:5},
31709             cmargins:{left:5,right:5,bottom:5,top:5}
31710         }, c.east) : false,
31711         center: Roo.apply({
31712             tabPosition: 'top',
31713             autoScroll:false,
31714             closeOnTab: true,
31715             titlebar:false,
31716             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31717         }, c.center)
31718     });
31719
31720     this.el.addClass('x-reader');
31721
31722     this.beginUpdate();
31723
31724     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31725         south: c.preview !== false ? Roo.apply({
31726             split:true,
31727             initialSize: 200,
31728             minSize: 100,
31729             autoScroll:true,
31730             collapsible:true,
31731             titlebar: true,
31732             cmargins:{top:5,left:0, right:0, bottom:0}
31733         }, c.preview) : false,
31734         center: Roo.apply({
31735             autoScroll:false,
31736             titlebar:false,
31737             minHeight:200
31738         }, c.listView)
31739     });
31740     this.add('center', new Roo.NestedLayoutPanel(inner,
31741             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31742
31743     this.endUpdate();
31744
31745     this.regions.preview = inner.getRegion('south');
31746     this.regions.listView = inner.getRegion('center');
31747 };
31748
31749 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31750  * Based on:
31751  * Ext JS Library 1.1.1
31752  * Copyright(c) 2006-2007, Ext JS, LLC.
31753  *
31754  * Originally Released Under LGPL - original licence link has changed is not relivant.
31755  *
31756  * Fork - LGPL
31757  * <script type="text/javascript">
31758  */
31759  
31760 /**
31761  * @class Roo.grid.Grid
31762  * @extends Roo.util.Observable
31763  * This class represents the primary interface of a component based grid control.
31764  * <br><br>Usage:<pre><code>
31765  var grid = new Roo.grid.Grid("my-container-id", {
31766      ds: myDataStore,
31767      cm: myColModel,
31768      selModel: mySelectionModel,
31769      autoSizeColumns: true,
31770      monitorWindowResize: false,
31771      trackMouseOver: true
31772  });
31773  // set any options
31774  grid.render();
31775  * </code></pre>
31776  * <b>Common Problems:</b><br/>
31777  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31778  * element will correct this<br/>
31779  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31780  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31781  * are unpredictable.<br/>
31782  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31783  * grid to calculate dimensions/offsets.<br/>
31784   * @constructor
31785  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31786  * The container MUST have some type of size defined for the grid to fill. The container will be
31787  * automatically set to position relative if it isn't already.
31788  * @param {Object} config A config object that sets properties on this grid.
31789  */
31790 Roo.grid.Grid = function(container, config){
31791         // initialize the container
31792         this.container = Roo.get(container);
31793         this.container.update("");
31794         this.container.setStyle("overflow", "hidden");
31795     this.container.addClass('x-grid-container');
31796
31797     this.id = this.container.id;
31798
31799     Roo.apply(this, config);
31800     // check and correct shorthanded configs
31801     if(this.ds){
31802         this.dataSource = this.ds;
31803         delete this.ds;
31804     }
31805     if(this.cm){
31806         this.colModel = this.cm;
31807         delete this.cm;
31808     }
31809     if(this.sm){
31810         this.selModel = this.sm;
31811         delete this.sm;
31812     }
31813
31814     if (this.selModel) {
31815         this.selModel = Roo.factory(this.selModel, Roo.grid);
31816         this.sm = this.selModel;
31817         this.sm.xmodule = this.xmodule || false;
31818     }
31819     if (typeof(this.colModel.config) == 'undefined') {
31820         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31821         this.cm = this.colModel;
31822         this.cm.xmodule = this.xmodule || false;
31823     }
31824     if (this.dataSource) {
31825         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31826         this.ds = this.dataSource;
31827         this.ds.xmodule = this.xmodule || false;
31828         
31829     }
31830     
31831     
31832     
31833     if(this.width){
31834         this.container.setWidth(this.width);
31835     }
31836
31837     if(this.height){
31838         this.container.setHeight(this.height);
31839     }
31840     /** @private */
31841         this.addEvents({
31842         // raw events
31843         /**
31844          * @event click
31845          * The raw click event for the entire grid.
31846          * @param {Roo.EventObject} e
31847          */
31848         "click" : true,
31849         /**
31850          * @event dblclick
31851          * The raw dblclick event for the entire grid.
31852          * @param {Roo.EventObject} e
31853          */
31854         "dblclick" : true,
31855         /**
31856          * @event contextmenu
31857          * The raw contextmenu event for the entire grid.
31858          * @param {Roo.EventObject} e
31859          */
31860         "contextmenu" : true,
31861         /**
31862          * @event mousedown
31863          * The raw mousedown event for the entire grid.
31864          * @param {Roo.EventObject} e
31865          */
31866         "mousedown" : true,
31867         /**
31868          * @event mouseup
31869          * The raw mouseup event for the entire grid.
31870          * @param {Roo.EventObject} e
31871          */
31872         "mouseup" : true,
31873         /**
31874          * @event mouseover
31875          * The raw mouseover event for the entire grid.
31876          * @param {Roo.EventObject} e
31877          */
31878         "mouseover" : true,
31879         /**
31880          * @event mouseout
31881          * The raw mouseout event for the entire grid.
31882          * @param {Roo.EventObject} e
31883          */
31884         "mouseout" : true,
31885         /**
31886          * @event keypress
31887          * The raw keypress event for the entire grid.
31888          * @param {Roo.EventObject} e
31889          */
31890         "keypress" : true,
31891         /**
31892          * @event keydown
31893          * The raw keydown event for the entire grid.
31894          * @param {Roo.EventObject} e
31895          */
31896         "keydown" : true,
31897
31898         // custom events
31899
31900         /**
31901          * @event cellclick
31902          * Fires when a cell is clicked
31903          * @param {Grid} this
31904          * @param {Number} rowIndex
31905          * @param {Number} columnIndex
31906          * @param {Roo.EventObject} e
31907          */
31908         "cellclick" : true,
31909         /**
31910          * @event celldblclick
31911          * Fires when a cell is double clicked
31912          * @param {Grid} this
31913          * @param {Number} rowIndex
31914          * @param {Number} columnIndex
31915          * @param {Roo.EventObject} e
31916          */
31917         "celldblclick" : true,
31918         /**
31919          * @event rowclick
31920          * Fires when a row is clicked
31921          * @param {Grid} this
31922          * @param {Number} rowIndex
31923          * @param {Roo.EventObject} e
31924          */
31925         "rowclick" : true,
31926         /**
31927          * @event rowdblclick
31928          * Fires when a row is double clicked
31929          * @param {Grid} this
31930          * @param {Number} rowIndex
31931          * @param {Roo.EventObject} e
31932          */
31933         "rowdblclick" : true,
31934         /**
31935          * @event headerclick
31936          * Fires when a header is clicked
31937          * @param {Grid} this
31938          * @param {Number} columnIndex
31939          * @param {Roo.EventObject} e
31940          */
31941         "headerclick" : true,
31942         /**
31943          * @event headerdblclick
31944          * Fires when a header cell is double clicked
31945          * @param {Grid} this
31946          * @param {Number} columnIndex
31947          * @param {Roo.EventObject} e
31948          */
31949         "headerdblclick" : true,
31950         /**
31951          * @event rowcontextmenu
31952          * Fires when a row is right clicked
31953          * @param {Grid} this
31954          * @param {Number} rowIndex
31955          * @param {Roo.EventObject} e
31956          */
31957         "rowcontextmenu" : true,
31958         /**
31959          * @event cellcontextmenu
31960          * Fires when a cell is right clicked
31961          * @param {Grid} this
31962          * @param {Number} rowIndex
31963          * @param {Number} cellIndex
31964          * @param {Roo.EventObject} e
31965          */
31966          "cellcontextmenu" : true,
31967         /**
31968          * @event headercontextmenu
31969          * Fires when a header is right clicked
31970          * @param {Grid} this
31971          * @param {Number} columnIndex
31972          * @param {Roo.EventObject} e
31973          */
31974         "headercontextmenu" : true,
31975         /**
31976          * @event bodyscroll
31977          * Fires when the body element is scrolled
31978          * @param {Number} scrollLeft
31979          * @param {Number} scrollTop
31980          */
31981         "bodyscroll" : true,
31982         /**
31983          * @event columnresize
31984          * Fires when the user resizes a column
31985          * @param {Number} columnIndex
31986          * @param {Number} newSize
31987          */
31988         "columnresize" : true,
31989         /**
31990          * @event columnmove
31991          * Fires when the user moves a column
31992          * @param {Number} oldIndex
31993          * @param {Number} newIndex
31994          */
31995         "columnmove" : true,
31996         /**
31997          * @event startdrag
31998          * Fires when row(s) start being dragged
31999          * @param {Grid} this
32000          * @param {Roo.GridDD} dd The drag drop object
32001          * @param {event} e The raw browser event
32002          */
32003         "startdrag" : true,
32004         /**
32005          * @event enddrag
32006          * Fires when a drag operation is complete
32007          * @param {Grid} this
32008          * @param {Roo.GridDD} dd The drag drop object
32009          * @param {event} e The raw browser event
32010          */
32011         "enddrag" : true,
32012         /**
32013          * @event dragdrop
32014          * Fires when dragged row(s) are dropped on a valid DD target
32015          * @param {Grid} this
32016          * @param {Roo.GridDD} dd The drag drop object
32017          * @param {String} targetId The target drag drop object
32018          * @param {event} e The raw browser event
32019          */
32020         "dragdrop" : true,
32021         /**
32022          * @event dragover
32023          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32024          * @param {Grid} this
32025          * @param {Roo.GridDD} dd The drag drop object
32026          * @param {String} targetId The target drag drop object
32027          * @param {event} e The raw browser event
32028          */
32029         "dragover" : true,
32030         /**
32031          * @event dragenter
32032          *  Fires when the dragged row(s) first cross another DD target while being dragged
32033          * @param {Grid} this
32034          * @param {Roo.GridDD} dd The drag drop object
32035          * @param {String} targetId The target drag drop object
32036          * @param {event} e The raw browser event
32037          */
32038         "dragenter" : true,
32039         /**
32040          * @event dragout
32041          * Fires when the dragged row(s) leave another DD target while being dragged
32042          * @param {Grid} this
32043          * @param {Roo.GridDD} dd The drag drop object
32044          * @param {String} targetId The target drag drop object
32045          * @param {event} e The raw browser event
32046          */
32047         "dragout" : true,
32048         /**
32049          * @event rowclass
32050          * Fires when a row is rendered, so you can change add a style to it.
32051          * @param {GridView} gridview The grid view
32052          * @param {Object} rowcfg contains record, rowIndex and rowClass - set rowClass to add a style.
32053          */
32054         'rowclass' : true,
32055
32056         /**
32057          * @event render
32058          * Fires when the grid is rendered
32059          * @param {Grid} grid
32060          */
32061         'render' : true
32062     });
32063
32064     Roo.grid.Grid.superclass.constructor.call(this);
32065 };
32066 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32067     
32068     /**
32069      * @cfg {String} ddGroup - drag drop group.
32070          */
32071     
32072     /**
32073      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32074          */
32075         minColumnWidth : 25,
32076
32077     /**
32078          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32079          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32080          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32081          */
32082         autoSizeColumns : false,
32083
32084         /**
32085          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32086          */
32087         autoSizeHeaders : true,
32088
32089         /**
32090          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32091          */
32092         monitorWindowResize : true,
32093
32094         /**
32095          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32096          * rows measured to get a columns size. Default is 0 (all rows).
32097          */
32098         maxRowsToMeasure : 0,
32099
32100         /**
32101          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32102          */
32103         trackMouseOver : true,
32104
32105     /**
32106          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32107          */
32108     
32109         /**
32110          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32111          */
32112         enableDragDrop : false,
32113
32114         /**
32115          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32116          */
32117         enableColumnMove : true,
32118
32119         /**
32120          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32121          */
32122         enableColumnHide : true,
32123
32124         /**
32125          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32126          */
32127         enableRowHeightSync : false,
32128
32129         /**
32130          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32131          */
32132         stripeRows : true,
32133
32134         /**
32135          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32136          */
32137         autoHeight : false,
32138
32139     /**
32140      * @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.
32141      */
32142     autoExpandColumn : false,
32143
32144     /**
32145     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32146     * Default is 50.
32147     */
32148     autoExpandMin : 50,
32149
32150     /**
32151     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32152     */
32153     autoExpandMax : 1000,
32154
32155     /**
32156          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32157          */
32158         view : null,
32159
32160         /**
32161      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32162          */
32163         loadMask : false,
32164     /**
32165      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32166          */
32167         dropTarget: false,
32168     // private
32169     rendered : false,
32170
32171     /**
32172     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32173     * of a fixed width. Default is false.
32174     */
32175     /**
32176     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32177     */
32178     /**
32179      * Called once after all setup has been completed and the grid is ready to be rendered.
32180      * @return {Roo.grid.Grid} this
32181      */
32182     render : function(){
32183         var c = this.container;
32184         // try to detect autoHeight/width mode
32185         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32186             this.autoHeight = true;
32187         }
32188         var view = this.getView();
32189         view.init(this);
32190
32191         c.on("click", this.onClick, this);
32192         c.on("dblclick", this.onDblClick, this);
32193         c.on("contextmenu", this.onContextMenu, this);
32194         c.on("keydown", this.onKeyDown, this);
32195
32196         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32197
32198         this.getSelectionModel().init(this);
32199
32200         view.render();
32201
32202         if(this.loadMask){
32203             this.loadMask = new Roo.LoadMask(this.container,
32204                     Roo.apply({store:this.dataSource}, this.loadMask));
32205         }
32206         
32207         
32208         if (this.toolbar && this.toolbar.xtype) {
32209             this.toolbar.container = this.getView().getHeaderPanel(true);
32210             this.toolbar = new Ext.Toolbar(this.toolbar);
32211         }
32212         if (this.footer && this.footer.xtype) {
32213             this.footer.dataSource = this.getDataSource();
32214             this.footer.container = this.getView().getFooterPanel(true);
32215             this.footer = Roo.factory(this.footer, Roo);
32216         }
32217         if (this.dropTarget && this.dropTarget.xtype) {
32218             delete this.dropTarget.xtype;
32219             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32220         }
32221         
32222         
32223         this.rendered = true;
32224         this.fireEvent('render', this);
32225         return this;
32226     },
32227
32228         /**
32229          * Reconfigures the grid to use a different Store and Column Model.
32230          * The View will be bound to the new objects and refreshed.
32231          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32232          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32233          */
32234     reconfigure : function(dataSource, colModel){
32235         if(this.loadMask){
32236             this.loadMask.destroy();
32237             this.loadMask = new Roo.LoadMask(this.container,
32238                     Roo.apply({store:dataSource}, this.loadMask));
32239         }
32240         this.view.bind(dataSource, colModel);
32241         this.dataSource = dataSource;
32242         this.colModel = colModel;
32243         this.view.refresh(true);
32244     },
32245
32246     // private
32247     onKeyDown : function(e){
32248         this.fireEvent("keydown", e);
32249     },
32250
32251     /**
32252      * Destroy this grid.
32253      * @param {Boolean} removeEl True to remove the element
32254      */
32255     destroy : function(removeEl, keepListeners){
32256         if(this.loadMask){
32257             this.loadMask.destroy();
32258         }
32259         var c = this.container;
32260         c.removeAllListeners();
32261         this.view.destroy();
32262         this.colModel.purgeListeners();
32263         if(!keepListeners){
32264             this.purgeListeners();
32265         }
32266         c.update("");
32267         if(removeEl === true){
32268             c.remove();
32269         }
32270     },
32271
32272     // private
32273     processEvent : function(name, e){
32274         this.fireEvent(name, e);
32275         var t = e.getTarget();
32276         var v = this.view;
32277         var header = v.findHeaderIndex(t);
32278         if(header !== false){
32279             this.fireEvent("header" + name, this, header, e);
32280         }else{
32281             var row = v.findRowIndex(t);
32282             var cell = v.findCellIndex(t);
32283             if(row !== false){
32284                 this.fireEvent("row" + name, this, row, e);
32285                 if(cell !== false){
32286                     this.fireEvent("cell" + name, this, row, cell, e);
32287                 }
32288             }
32289         }
32290     },
32291
32292     // private
32293     onClick : function(e){
32294         this.processEvent("click", e);
32295     },
32296
32297     // private
32298     onContextMenu : function(e, t){
32299         this.processEvent("contextmenu", e);
32300     },
32301
32302     // private
32303     onDblClick : function(e){
32304         this.processEvent("dblclick", e);
32305     },
32306
32307     // private
32308     walkCells : function(row, col, step, fn, scope){
32309         var cm = this.colModel, clen = cm.getColumnCount();
32310         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32311         if(step < 0){
32312             if(col < 0){
32313                 row--;
32314                 first = false;
32315             }
32316             while(row >= 0){
32317                 if(!first){
32318                     col = clen-1;
32319                 }
32320                 first = false;
32321                 while(col >= 0){
32322                     if(fn.call(scope || this, row, col, cm) === true){
32323                         return [row, col];
32324                     }
32325                     col--;
32326                 }
32327                 row--;
32328             }
32329         } else {
32330             if(col >= clen){
32331                 row++;
32332                 first = false;
32333             }
32334             while(row < rlen){
32335                 if(!first){
32336                     col = 0;
32337                 }
32338                 first = false;
32339                 while(col < clen){
32340                     if(fn.call(scope || this, row, col, cm) === true){
32341                         return [row, col];
32342                     }
32343                     col++;
32344                 }
32345                 row++;
32346             }
32347         }
32348         return null;
32349     },
32350
32351     // private
32352     getSelections : function(){
32353         return this.selModel.getSelections();
32354     },
32355
32356     /**
32357      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32358      * but if manual update is required this method will initiate it.
32359      */
32360     autoSize : function(){
32361         if(this.rendered){
32362             this.view.layout();
32363             if(this.view.adjustForScroll){
32364                 this.view.adjustForScroll();
32365             }
32366         }
32367     },
32368
32369     /**
32370      * Returns the grid's underlying element.
32371      * @return {Element} The element
32372      */
32373     getGridEl : function(){
32374         return this.container;
32375     },
32376
32377     // private for compatibility, overridden by editor grid
32378     stopEditing : function(){},
32379
32380     /**
32381      * Returns the grid's SelectionModel.
32382      * @return {SelectionModel}
32383      */
32384     getSelectionModel : function(){
32385         if(!this.selModel){
32386             this.selModel = new Roo.grid.RowSelectionModel();
32387         }
32388         return this.selModel;
32389     },
32390
32391     /**
32392      * Returns the grid's DataSource.
32393      * @return {DataSource}
32394      */
32395     getDataSource : function(){
32396         return this.dataSource;
32397     },
32398
32399     /**
32400      * Returns the grid's ColumnModel.
32401      * @return {ColumnModel}
32402      */
32403     getColumnModel : function(){
32404         return this.colModel;
32405     },
32406
32407     /**
32408      * Returns the grid's GridView object.
32409      * @return {GridView}
32410      */
32411     getView : function(){
32412         if(!this.view){
32413             this.view = new Roo.grid.GridView(this.viewConfig);
32414         }
32415         return this.view;
32416     },
32417     /**
32418      * Called to get grid's drag proxy text, by default returns this.ddText.
32419      * @return {String}
32420      */
32421     getDragDropText : function(){
32422         var count = this.selModel.getCount();
32423         return String.format(this.ddText, count, count == 1 ? '' : 's');
32424     }
32425 });
32426 /**
32427  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32428  * %0 is replaced with the number of selected rows.
32429  * @type String
32430  */
32431 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32432  * Based on:
32433  * Ext JS Library 1.1.1
32434  * Copyright(c) 2006-2007, Ext JS, LLC.
32435  *
32436  * Originally Released Under LGPL - original licence link has changed is not relivant.
32437  *
32438  * Fork - LGPL
32439  * <script type="text/javascript">
32440  */
32441  
32442 Roo.grid.AbstractGridView = function(){
32443         this.grid = null;
32444         
32445         this.events = {
32446             "beforerowremoved" : true,
32447             "beforerowsinserted" : true,
32448             "beforerefresh" : true,
32449             "rowremoved" : true,
32450             "rowsinserted" : true,
32451             "rowupdated" : true,
32452             "refresh" : true
32453         };
32454     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32455 };
32456
32457 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32458     rowClass : "x-grid-row",
32459     cellClass : "x-grid-cell",
32460     tdClass : "x-grid-td",
32461     hdClass : "x-grid-hd",
32462     splitClass : "x-grid-hd-split",
32463     
32464         init: function(grid){
32465         this.grid = grid;
32466                 var cid = this.grid.getGridEl().id;
32467         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32468         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32469         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32470         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32471         },
32472         
32473         getColumnRenderers : function(){
32474         var renderers = [];
32475         var cm = this.grid.colModel;
32476         var colCount = cm.getColumnCount();
32477         for(var i = 0; i < colCount; i++){
32478             renderers[i] = cm.getRenderer(i);
32479         }
32480         return renderers;
32481     },
32482     
32483     getColumnIds : function(){
32484         var ids = [];
32485         var cm = this.grid.colModel;
32486         var colCount = cm.getColumnCount();
32487         for(var i = 0; i < colCount; i++){
32488             ids[i] = cm.getColumnId(i);
32489         }
32490         return ids;
32491     },
32492     
32493     getDataIndexes : function(){
32494         if(!this.indexMap){
32495             this.indexMap = this.buildIndexMap();
32496         }
32497         return this.indexMap.colToData;
32498     },
32499     
32500     getColumnIndexByDataIndex : function(dataIndex){
32501         if(!this.indexMap){
32502             this.indexMap = this.buildIndexMap();
32503         }
32504         return this.indexMap.dataToCol[dataIndex];
32505     },
32506     
32507     /**
32508      * Set a css style for a column dynamically. 
32509      * @param {Number} colIndex The index of the column
32510      * @param {String} name The css property name
32511      * @param {String} value The css value
32512      */
32513     setCSSStyle : function(colIndex, name, value){
32514         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32515         Roo.util.CSS.updateRule(selector, name, value);
32516     },
32517     
32518     generateRules : function(cm){
32519         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32520         Roo.util.CSS.removeStyleSheet(rulesId);
32521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32522             var cid = cm.getColumnId(i);
32523             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32524                          this.tdSelector, cid, " {\n}\n",
32525                          this.hdSelector, cid, " {\n}\n",
32526                          this.splitSelector, cid, " {\n}\n");
32527         }
32528         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32529     }
32530 });/*
32531  * Based on:
32532  * Ext JS Library 1.1.1
32533  * Copyright(c) 2006-2007, Ext JS, LLC.
32534  *
32535  * Originally Released Under LGPL - original licence link has changed is not relivant.
32536  *
32537  * Fork - LGPL
32538  * <script type="text/javascript">
32539  */
32540
32541 // private
32542 // This is a support class used internally by the Grid components
32543 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32544     this.grid = grid;
32545     this.view = grid.getView();
32546     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32547     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32548     if(hd2){
32549         this.setHandleElId(Roo.id(hd));
32550         this.setOuterHandleElId(Roo.id(hd2));
32551     }
32552     this.scroll = false;
32553 };
32554 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32555     maxDragWidth: 120,
32556     getDragData : function(e){
32557         var t = Roo.lib.Event.getTarget(e);
32558         var h = this.view.findHeaderCell(t);
32559         if(h){
32560             return {ddel: h.firstChild, header:h};
32561         }
32562         return false;
32563     },
32564
32565     onInitDrag : function(e){
32566         this.view.headersDisabled = true;
32567         var clone = this.dragData.ddel.cloneNode(true);
32568         clone.id = Roo.id();
32569         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32570         this.proxy.update(clone);
32571         return true;
32572     },
32573
32574     afterValidDrop : function(){
32575         var v = this.view;
32576         setTimeout(function(){
32577             v.headersDisabled = false;
32578         }, 50);
32579     },
32580
32581     afterInvalidDrop : function(){
32582         var v = this.view;
32583         setTimeout(function(){
32584             v.headersDisabled = false;
32585         }, 50);
32586     }
32587 });
32588 /*
32589  * Based on:
32590  * Ext JS Library 1.1.1
32591  * Copyright(c) 2006-2007, Ext JS, LLC.
32592  *
32593  * Originally Released Under LGPL - original licence link has changed is not relivant.
32594  *
32595  * Fork - LGPL
32596  * <script type="text/javascript">
32597  */
32598 // private
32599 // This is a support class used internally by the Grid components
32600 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32601     this.grid = grid;
32602     this.view = grid.getView();
32603     // split the proxies so they don't interfere with mouse events
32604     this.proxyTop = Roo.DomHelper.append(document.body, {
32605         cls:"col-move-top", html:"&#160;"
32606     }, true);
32607     this.proxyBottom = Roo.DomHelper.append(document.body, {
32608         cls:"col-move-bottom", html:"&#160;"
32609     }, true);
32610     this.proxyTop.hide = this.proxyBottom.hide = function(){
32611         this.setLeftTop(-100,-100);
32612         this.setStyle("visibility", "hidden");
32613     };
32614     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32615     // temporarily disabled
32616     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32617     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32618 };
32619 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32620     proxyOffsets : [-4, -9],
32621     fly: Roo.Element.fly,
32622
32623     getTargetFromEvent : function(e){
32624         var t = Roo.lib.Event.getTarget(e);
32625         var cindex = this.view.findCellIndex(t);
32626         if(cindex !== false){
32627             return this.view.getHeaderCell(cindex);
32628         }
32629     },
32630
32631     nextVisible : function(h){
32632         var v = this.view, cm = this.grid.colModel;
32633         h = h.nextSibling;
32634         while(h){
32635             if(!cm.isHidden(v.getCellIndex(h))){
32636                 return h;
32637             }
32638             h = h.nextSibling;
32639         }
32640         return null;
32641     },
32642
32643     prevVisible : function(h){
32644         var v = this.view, cm = this.grid.colModel;
32645         h = h.prevSibling;
32646         while(h){
32647             if(!cm.isHidden(v.getCellIndex(h))){
32648                 return h;
32649             }
32650             h = h.prevSibling;
32651         }
32652         return null;
32653     },
32654
32655     positionIndicator : function(h, n, e){
32656         var x = Roo.lib.Event.getPageX(e);
32657         var r = Roo.lib.Dom.getRegion(n.firstChild);
32658         var px, pt, py = r.top + this.proxyOffsets[1];
32659         if((r.right - x) <= (r.right-r.left)/2){
32660             px = r.right+this.view.borderWidth;
32661             pt = "after";
32662         }else{
32663             px = r.left;
32664             pt = "before";
32665         }
32666         var oldIndex = this.view.getCellIndex(h);
32667         var newIndex = this.view.getCellIndex(n);
32668
32669         if(this.grid.colModel.isFixed(newIndex)){
32670             return false;
32671         }
32672
32673         var locked = this.grid.colModel.isLocked(newIndex);
32674
32675         if(pt == "after"){
32676             newIndex++;
32677         }
32678         if(oldIndex < newIndex){
32679             newIndex--;
32680         }
32681         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32682             return false;
32683         }
32684         px +=  this.proxyOffsets[0];
32685         this.proxyTop.setLeftTop(px, py);
32686         this.proxyTop.show();
32687         if(!this.bottomOffset){
32688             this.bottomOffset = this.view.mainHd.getHeight();
32689         }
32690         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32691         this.proxyBottom.show();
32692         return pt;
32693     },
32694
32695     onNodeEnter : function(n, dd, e, data){
32696         if(data.header != n){
32697             this.positionIndicator(data.header, n, e);
32698         }
32699     },
32700
32701     onNodeOver : function(n, dd, e, data){
32702         var result = false;
32703         if(data.header != n){
32704             result = this.positionIndicator(data.header, n, e);
32705         }
32706         if(!result){
32707             this.proxyTop.hide();
32708             this.proxyBottom.hide();
32709         }
32710         return result ? this.dropAllowed : this.dropNotAllowed;
32711     },
32712
32713     onNodeOut : function(n, dd, e, data){
32714         this.proxyTop.hide();
32715         this.proxyBottom.hide();
32716     },
32717
32718     onNodeDrop : function(n, dd, e, data){
32719         var h = data.header;
32720         if(h != n){
32721             var cm = this.grid.colModel;
32722             var x = Roo.lib.Event.getPageX(e);
32723             var r = Roo.lib.Dom.getRegion(n.firstChild);
32724             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32725             var oldIndex = this.view.getCellIndex(h);
32726             var newIndex = this.view.getCellIndex(n);
32727             var locked = cm.isLocked(newIndex);
32728             if(pt == "after"){
32729                 newIndex++;
32730             }
32731             if(oldIndex < newIndex){
32732                 newIndex--;
32733             }
32734             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32735                 return false;
32736             }
32737             cm.setLocked(oldIndex, locked, true);
32738             cm.moveColumn(oldIndex, newIndex);
32739             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32740             return true;
32741         }
32742         return false;
32743     }
32744 });
32745 /*
32746  * Based on:
32747  * Ext JS Library 1.1.1
32748  * Copyright(c) 2006-2007, Ext JS, LLC.
32749  *
32750  * Originally Released Under LGPL - original licence link has changed is not relivant.
32751  *
32752  * Fork - LGPL
32753  * <script type="text/javascript">
32754  */
32755   
32756 /**
32757  * @class Roo.grid.GridView
32758  * @extends Roo.util.Observable
32759  *
32760  * @constructor
32761  * @param {Object} config
32762  */
32763 Roo.grid.GridView = function(config){
32764     Roo.grid.GridView.superclass.constructor.call(this);
32765     this.el = null;
32766
32767     Roo.apply(this, config);
32768 };
32769
32770 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32771
32772     /**
32773      * Override this function to apply custom css classes to rows during rendering
32774      * @param {Record} record The record
32775      * @param {Number} index
32776      * @method getRowClass
32777      */
32778     rowClass : "x-grid-row",
32779
32780     cellClass : "x-grid-col",
32781
32782     tdClass : "x-grid-td",
32783
32784     hdClass : "x-grid-hd",
32785
32786     splitClass : "x-grid-split",
32787
32788     sortClasses : ["sort-asc", "sort-desc"],
32789
32790     enableMoveAnim : false,
32791
32792     hlColor: "C3DAF9",
32793
32794     dh : Roo.DomHelper,
32795
32796     fly : Roo.Element.fly,
32797
32798     css : Roo.util.CSS,
32799
32800     borderWidth: 1,
32801
32802     splitOffset: 3,
32803
32804     scrollIncrement : 22,
32805
32806     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32807
32808     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32809
32810     bind : function(ds, cm){
32811         if(this.ds){
32812             this.ds.un("load", this.onLoad, this);
32813             this.ds.un("datachanged", this.onDataChange, this);
32814             this.ds.un("add", this.onAdd, this);
32815             this.ds.un("remove", this.onRemove, this);
32816             this.ds.un("update", this.onUpdate, this);
32817             this.ds.un("clear", this.onClear, this);
32818         }
32819         if(ds){
32820             ds.on("load", this.onLoad, this);
32821             ds.on("datachanged", this.onDataChange, this);
32822             ds.on("add", this.onAdd, this);
32823             ds.on("remove", this.onRemove, this);
32824             ds.on("update", this.onUpdate, this);
32825             ds.on("clear", this.onClear, this);
32826         }
32827         this.ds = ds;
32828
32829         if(this.cm){
32830             this.cm.un("widthchange", this.onColWidthChange, this);
32831             this.cm.un("headerchange", this.onHeaderChange, this);
32832             this.cm.un("hiddenchange", this.onHiddenChange, this);
32833             this.cm.un("columnmoved", this.onColumnMove, this);
32834             this.cm.un("columnlockchange", this.onColumnLock, this);
32835         }
32836         if(cm){
32837             this.generateRules(cm);
32838             cm.on("widthchange", this.onColWidthChange, this);
32839             cm.on("headerchange", this.onHeaderChange, this);
32840             cm.on("hiddenchange", this.onHiddenChange, this);
32841             cm.on("columnmoved", this.onColumnMove, this);
32842             cm.on("columnlockchange", this.onColumnLock, this);
32843         }
32844         this.cm = cm;
32845     },
32846
32847     init: function(grid){
32848                 Roo.grid.GridView.superclass.init.call(this, grid);
32849
32850                 this.bind(grid.dataSource, grid.colModel);
32851
32852             grid.on("headerclick", this.handleHeaderClick, this);
32853
32854         if(grid.trackMouseOver){
32855             grid.on("mouseover", this.onRowOver, this);
32856                 grid.on("mouseout", this.onRowOut, this);
32857             }
32858             grid.cancelTextSelection = function(){};
32859                 this.gridId = grid.id;
32860
32861                 var tpls = this.templates || {};
32862
32863                 if(!tpls.master){
32864                     tpls.master = new Roo.Template(
32865                        '<div class="x-grid" hidefocus="true">',
32866                           '<div class="x-grid-topbar"></div>',
32867                           '<div class="x-grid-scroller"><div></div></div>',
32868                           '<div class="x-grid-locked">',
32869                               '<div class="x-grid-header">{lockedHeader}</div>',
32870                               '<div class="x-grid-body">{lockedBody}</div>',
32871                           "</div>",
32872                           '<div class="x-grid-viewport">',
32873                               '<div class="x-grid-header">{header}</div>',
32874                               '<div class="x-grid-body">{body}</div>',
32875                           "</div>",
32876                           '<div class="x-grid-bottombar"></div>',
32877                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32878                           '<div class="x-grid-resize-proxy">&#160;</div>',
32879                        "</div>"
32880                     );
32881                     tpls.master.disableformats = true;
32882                 }
32883
32884                 if(!tpls.header){
32885                     tpls.header = new Roo.Template(
32886                        '<table border="0" cellspacing="0" cellpadding="0">',
32887                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32888                        "</table>{splits}"
32889                     );
32890                     tpls.header.disableformats = true;
32891                 }
32892                 tpls.header.compile();
32893
32894                 if(!tpls.hcell){
32895                     tpls.hcell = new Roo.Template(
32896                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32897                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32898                         "</div></td>"
32899                      );
32900                      tpls.hcell.disableFormats = true;
32901                 }
32902                 tpls.hcell.compile();
32903
32904                 if(!tpls.hsplit){
32905                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32906                     tpls.hsplit.disableFormats = true;
32907                 }
32908                 tpls.hsplit.compile();
32909
32910                 if(!tpls.body){
32911                     tpls.body = new Roo.Template(
32912                        '<table border="0" cellspacing="0" cellpadding="0">',
32913                        "<tbody>{rows}</tbody>",
32914                        "</table>"
32915                     );
32916                     tpls.body.disableFormats = true;
32917                 }
32918                 tpls.body.compile();
32919
32920                 if(!tpls.row){
32921                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32922                     tpls.row.disableFormats = true;
32923                 }
32924                 tpls.row.compile();
32925
32926                 if(!tpls.cell){
32927                     tpls.cell = new Roo.Template(
32928                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32929                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32930                         "</td>"
32931                     );
32932             tpls.cell.disableFormats = true;
32933         }
32934                 tpls.cell.compile();
32935
32936                 this.templates = tpls;
32937         },
32938
32939         // remap these for backwards compat
32940     onColWidthChange : function(){
32941         this.updateColumns.apply(this, arguments);
32942     },
32943     onHeaderChange : function(){
32944         this.updateHeaders.apply(this, arguments);
32945     }, 
32946     onHiddenChange : function(){
32947         this.handleHiddenChange.apply(this, arguments);
32948     },
32949     onColumnMove : function(){
32950         this.handleColumnMove.apply(this, arguments);
32951     },
32952     onColumnLock : function(){
32953         this.handleLockChange.apply(this, arguments);
32954     },
32955
32956     onDataChange : function(){
32957         this.refresh();
32958         this.updateHeaderSortState();
32959     },
32960
32961         onClear : function(){
32962         this.refresh();
32963     },
32964
32965         onUpdate : function(ds, record){
32966         this.refreshRow(record);
32967     },
32968
32969     refreshRow : function(record){
32970         var ds = this.ds, index;
32971         if(typeof record == 'number'){
32972             index = record;
32973             record = ds.getAt(index);
32974         }else{
32975             index = ds.indexOf(record);
32976         }
32977         this.insertRows(ds, index, index, true);
32978         this.onRemove(ds, record, index+1, true);
32979         this.syncRowHeights(index, index);
32980         this.layout();
32981         this.fireEvent("rowupdated", this, index, record);
32982     },
32983
32984     onAdd : function(ds, records, index){
32985         this.insertRows(ds, index, index + (records.length-1));
32986     },
32987
32988     onRemove : function(ds, record, index, isUpdate){
32989         if(isUpdate !== true){
32990             this.fireEvent("beforerowremoved", this, index, record);
32991         }
32992         var bt = this.getBodyTable(), lt = this.getLockedTable();
32993         if(bt.rows[index]){
32994             bt.firstChild.removeChild(bt.rows[index]);
32995         }
32996         if(lt.rows[index]){
32997             lt.firstChild.removeChild(lt.rows[index]);
32998         }
32999         if(isUpdate !== true){
33000             this.stripeRows(index);
33001             this.syncRowHeights(index, index);
33002             this.layout();
33003             this.fireEvent("rowremoved", this, index, record);
33004         }
33005     },
33006
33007     onLoad : function(){
33008         this.scrollToTop();
33009     },
33010
33011     /**
33012      * Scrolls the grid to the top
33013      */
33014     scrollToTop : function(){
33015         if(this.scroller){
33016             this.scroller.dom.scrollTop = 0;
33017             this.syncScroll();
33018         }
33019     },
33020
33021     /**
33022      * Gets a panel in the header of the grid that can be used for toolbars etc.
33023      * After modifying the contents of this panel a call to grid.autoSize() may be
33024      * required to register any changes in size.
33025      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33026      * @return Roo.Element
33027      */
33028     getHeaderPanel : function(doShow){
33029         if(doShow){
33030             this.headerPanel.show();
33031         }
33032         return this.headerPanel;
33033         },
33034
33035         /**
33036      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33037      * After modifying the contents of this panel a call to grid.autoSize() may be
33038      * required to register any changes in size.
33039      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33040      * @return Roo.Element
33041      */
33042     getFooterPanel : function(doShow){
33043         if(doShow){
33044             this.footerPanel.show();
33045         }
33046         return this.footerPanel;
33047         },
33048
33049         initElements : function(){
33050             var E = Roo.Element;
33051             var el = this.grid.getGridEl().dom.firstChild;
33052             var cs = el.childNodes;
33053
33054             this.el = new E(el);
33055             this.headerPanel = new E(el.firstChild);
33056             this.headerPanel.enableDisplayMode("block");
33057
33058         this.scroller = new E(cs[1]);
33059             this.scrollSizer = new E(this.scroller.dom.firstChild);
33060
33061             this.lockedWrap = new E(cs[2]);
33062             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33063             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33064
33065             this.mainWrap = new E(cs[3]);
33066             this.mainHd = new E(this.mainWrap.dom.firstChild);
33067             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33068
33069             this.footerPanel = new E(cs[4]);
33070             this.footerPanel.enableDisplayMode("block");
33071
33072         this.focusEl = new E(cs[5]);
33073         this.focusEl.swallowEvent("click", true);
33074         this.resizeProxy = new E(cs[6]);
33075
33076             this.headerSelector = String.format(
33077                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33078                this.lockedHd.id, this.mainHd.id
33079             );
33080
33081             this.splitterSelector = String.format(
33082                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33083                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33084             );
33085     },
33086     idToCssName : function(s)
33087     {
33088         return s.replace(/[^a-z0-9]+/ig, '-');
33089     },
33090
33091         getHeaderCell : function(index){
33092             return Roo.DomQuery.select(this.headerSelector)[index];
33093         },
33094
33095         getHeaderCellMeasure : function(index){
33096             return this.getHeaderCell(index).firstChild;
33097         },
33098
33099         getHeaderCellText : function(index){
33100             return this.getHeaderCell(index).firstChild.firstChild;
33101         },
33102
33103         getLockedTable : function(){
33104             return this.lockedBody.dom.firstChild;
33105         },
33106
33107         getBodyTable : function(){
33108             return this.mainBody.dom.firstChild;
33109         },
33110
33111         getLockedRow : function(index){
33112             return this.getLockedTable().rows[index];
33113         },
33114
33115         getRow : function(index){
33116             return this.getBodyTable().rows[index];
33117         },
33118
33119         getRowComposite : function(index){
33120             if(!this.rowEl){
33121                 this.rowEl = new Roo.CompositeElementLite();
33122             }
33123         var els = [], lrow, mrow;
33124         if(lrow = this.getLockedRow(index)){
33125             els.push(lrow);
33126         }
33127         if(mrow = this.getRow(index)){
33128             els.push(mrow);
33129         }
33130         this.rowEl.elements = els;
33131             return this.rowEl;
33132         },
33133
33134         getCell : function(rowIndex, colIndex){
33135             var locked = this.cm.getLockedCount();
33136             var source;
33137             if(colIndex < locked){
33138                 source = this.lockedBody.dom.firstChild;
33139             }else{
33140                 source = this.mainBody.dom.firstChild;
33141                 colIndex -= locked;
33142             }
33143         return source.rows[rowIndex].childNodes[colIndex];
33144         },
33145
33146         getCellText : function(rowIndex, colIndex){
33147             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33148         },
33149
33150         getCellBox : function(cell){
33151             var b = this.fly(cell).getBox();
33152         if(Roo.isOpera){ // opera fails to report the Y
33153             b.y = cell.offsetTop + this.mainBody.getY();
33154         }
33155         return b;
33156     },
33157
33158     getCellIndex : function(cell){
33159         var id = String(cell.className).match(this.cellRE);
33160         if(id){
33161             return parseInt(id[1], 10);
33162         }
33163         return 0;
33164     },
33165
33166     findHeaderIndex : function(n){
33167         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33168         return r ? this.getCellIndex(r) : false;
33169     },
33170
33171     findHeaderCell : function(n){
33172         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33173         return r ? r : false;
33174     },
33175
33176     findRowIndex : function(n){
33177         if(!n){
33178             return false;
33179         }
33180         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33181         return r ? r.rowIndex : false;
33182     },
33183
33184     findCellIndex : function(node){
33185         var stop = this.el.dom;
33186         while(node && node != stop){
33187             if(this.findRE.test(node.className)){
33188                 return this.getCellIndex(node);
33189             }
33190             node = node.parentNode;
33191         }
33192         return false;
33193     },
33194
33195     getColumnId : function(index){
33196             return this.cm.getColumnId(index);
33197         },
33198
33199         getSplitters : function(){
33200             if(this.splitterSelector){
33201                return Roo.DomQuery.select(this.splitterSelector);
33202             }else{
33203                 return null;
33204             }
33205         },
33206
33207         getSplitter : function(index){
33208             return this.getSplitters()[index];
33209         },
33210
33211     onRowOver : function(e, t){
33212         var row;
33213         if((row = this.findRowIndex(t)) !== false){
33214             this.getRowComposite(row).addClass("x-grid-row-over");
33215         }
33216     },
33217
33218     onRowOut : function(e, t){
33219         var row;
33220         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33221             this.getRowComposite(row).removeClass("x-grid-row-over");
33222         }
33223     },
33224
33225     renderHeaders : function(){
33226             var cm = this.cm;
33227         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33228         var cb = [], lb = [], sb = [], lsb = [], p = {};
33229         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33230             p.cellId = "x-grid-hd-0-" + i;
33231             p.splitId = "x-grid-csplit-0-" + i;
33232             p.id = cm.getColumnId(i);
33233             p.title = cm.getColumnTooltip(i) || "";
33234             p.value = cm.getColumnHeader(i) || "";
33235             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33236             if(!cm.isLocked(i)){
33237                 cb[cb.length] = ct.apply(p);
33238                 sb[sb.length] = st.apply(p);
33239             }else{
33240                 lb[lb.length] = ct.apply(p);
33241                 lsb[lsb.length] = st.apply(p);
33242             }
33243         }
33244         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33245                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33246         },
33247
33248         updateHeaders : function(){
33249         var html = this.renderHeaders();
33250         this.lockedHd.update(html[0]);
33251         this.mainHd.update(html[1]);
33252     },
33253
33254     /**
33255      * Focuses the specified row.
33256      * @param {Number} row The row index
33257      */
33258     focusRow : function(row){
33259         var x = this.scroller.dom.scrollLeft;
33260         this.focusCell(row, 0, false);
33261         this.scroller.dom.scrollLeft = x;
33262     },
33263
33264     /**
33265      * Focuses the specified cell.
33266      * @param {Number} row The row index
33267      * @param {Number} col The column index
33268      * @param {Boolean} hscroll false to disable horizontal scrolling
33269      */
33270     focusCell : function(row, col, hscroll){
33271         var el = this.ensureVisible(row, col, hscroll);
33272         this.focusEl.alignTo(el, "tl-tl");
33273         if(Roo.isGecko){
33274             this.focusEl.focus();
33275         }else{
33276             this.focusEl.focus.defer(1, this.focusEl);
33277         }
33278     },
33279
33280     /**
33281      * Scrolls the specified cell into view
33282      * @param {Number} row The row index
33283      * @param {Number} col The column index
33284      * @param {Boolean} hscroll false to disable horizontal scrolling
33285      */
33286     ensureVisible : function(row, col, hscroll){
33287         if(typeof row != "number"){
33288             row = row.rowIndex;
33289         }
33290         if(row < 0 && row >= this.ds.getCount()){
33291             return;
33292         }
33293         col = (col !== undefined ? col : 0);
33294         var cm = this.grid.colModel;
33295         while(cm.isHidden(col)){
33296             col++;
33297         }
33298
33299         var el = this.getCell(row, col);
33300         if(!el){
33301             return;
33302         }
33303         var c = this.scroller.dom;
33304
33305         var ctop = parseInt(el.offsetTop, 10);
33306         var cleft = parseInt(el.offsetLeft, 10);
33307         var cbot = ctop + el.offsetHeight;
33308         var cright = cleft + el.offsetWidth;
33309
33310         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33311         var stop = parseInt(c.scrollTop, 10);
33312         var sleft = parseInt(c.scrollLeft, 10);
33313         var sbot = stop + ch;
33314         var sright = sleft + c.clientWidth;
33315
33316         if(ctop < stop){
33317                 c.scrollTop = ctop;
33318         }else if(cbot > sbot){
33319             c.scrollTop = cbot-ch;
33320         }
33321
33322         if(hscroll !== false){
33323             if(cleft < sleft){
33324                 c.scrollLeft = cleft;
33325             }else if(cright > sright){
33326                 c.scrollLeft = cright-c.clientWidth;
33327             }
33328         }
33329         return el;
33330     },
33331
33332     updateColumns : function(){
33333         this.grid.stopEditing();
33334         var cm = this.grid.colModel, colIds = this.getColumnIds();
33335         //var totalWidth = cm.getTotalWidth();
33336         var pos = 0;
33337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33338             //if(cm.isHidden(i)) continue;
33339             var w = cm.getColumnWidth(i);
33340             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33341             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33342         }
33343         this.updateSplitters();
33344     },
33345
33346     generateRules : function(cm){
33347         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33348         Roo.util.CSS.removeStyleSheet(rulesId);
33349         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33350             var cid = cm.getColumnId(i);
33351             var align = '';
33352             if(cm.config[i].align){
33353                 align = 'text-align:'+cm.config[i].align+';';
33354             }
33355             var hidden = '';
33356             if(cm.isHidden(i)){
33357                 hidden = 'display:none;';
33358             }
33359             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33360             ruleBuf.push(
33361                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33362                     this.hdSelector, cid, " {\n", align, width, "}\n",
33363                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33364                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33365         }
33366         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33367     },
33368
33369     updateSplitters : function(){
33370         var cm = this.cm, s = this.getSplitters();
33371         if(s){ // splitters not created yet
33372             var pos = 0, locked = true;
33373             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33374                 if(cm.isHidden(i)) continue;
33375                 var w = cm.getColumnWidth(i);
33376                 if(!cm.isLocked(i) && locked){
33377                     pos = 0;
33378                     locked = false;
33379                 }
33380                 pos += w;
33381                 s[i].style.left = (pos-this.splitOffset) + "px";
33382             }
33383         }
33384     },
33385
33386     handleHiddenChange : function(colModel, colIndex, hidden){
33387         if(hidden){
33388             this.hideColumn(colIndex);
33389         }else{
33390             this.unhideColumn(colIndex);
33391         }
33392     },
33393
33394     hideColumn : function(colIndex){
33395         var cid = this.getColumnId(colIndex);
33396         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33397         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33398         if(Roo.isSafari){
33399             this.updateHeaders();
33400         }
33401         this.updateSplitters();
33402         this.layout();
33403     },
33404
33405     unhideColumn : function(colIndex){
33406         var cid = this.getColumnId(colIndex);
33407         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33408         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33409
33410         if(Roo.isSafari){
33411             this.updateHeaders();
33412         }
33413         this.updateSplitters();
33414         this.layout();
33415     },
33416
33417     insertRows : function(dm, firstRow, lastRow, isUpdate){
33418         if(firstRow == 0 && lastRow == dm.getCount()-1){
33419             this.refresh();
33420         }else{
33421             if(!isUpdate){
33422                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33423             }
33424             var s = this.getScrollState();
33425             var markup = this.renderRows(firstRow, lastRow);
33426             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33427             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33428             this.restoreScroll(s);
33429             if(!isUpdate){
33430                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33431                 this.syncRowHeights(firstRow, lastRow);
33432                 this.stripeRows(firstRow);
33433                 this.layout();
33434             }
33435         }
33436     },
33437
33438     bufferRows : function(markup, target, index){
33439         var before = null, trows = target.rows, tbody = target.tBodies[0];
33440         if(index < trows.length){
33441             before = trows[index];
33442         }
33443         var b = document.createElement("div");
33444         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33445         var rows = b.firstChild.rows;
33446         for(var i = 0, len = rows.length; i < len; i++){
33447             if(before){
33448                 tbody.insertBefore(rows[0], before);
33449             }else{
33450                 tbody.appendChild(rows[0]);
33451             }
33452         }
33453         b.innerHTML = "";
33454         b = null;
33455     },
33456
33457     deleteRows : function(dm, firstRow, lastRow){
33458         if(dm.getRowCount()<1){
33459             this.fireEvent("beforerefresh", this);
33460             this.mainBody.update("");
33461             this.lockedBody.update("");
33462             this.fireEvent("refresh", this);
33463         }else{
33464             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33465             var bt = this.getBodyTable();
33466             var tbody = bt.firstChild;
33467             var rows = bt.rows;
33468             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33469                 tbody.removeChild(rows[firstRow]);
33470             }
33471             this.stripeRows(firstRow);
33472             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33473         }
33474     },
33475
33476     updateRows : function(dataSource, firstRow, lastRow){
33477         var s = this.getScrollState();
33478         this.refresh();
33479         this.restoreScroll(s);
33480     },
33481
33482     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33483         if(!noRefresh){
33484            this.refresh();
33485         }
33486         this.updateHeaderSortState();
33487     },
33488
33489     getScrollState : function(){
33490         var sb = this.scroller.dom;
33491         return {left: sb.scrollLeft, top: sb.scrollTop};
33492     },
33493
33494     stripeRows : function(startRow){
33495         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33496             return;
33497         }
33498         startRow = startRow || 0;
33499         var rows = this.getBodyTable().rows;
33500         var lrows = this.getLockedTable().rows;
33501         var cls = ' x-grid-row-alt ';
33502         for(var i = startRow, len = rows.length; i < len; i++){
33503             var row = rows[i], lrow = lrows[i];
33504             var isAlt = ((i+1) % 2 == 0);
33505             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33506             if(isAlt == hasAlt){
33507                 continue;
33508             }
33509             if(isAlt){
33510                 row.className += " x-grid-row-alt";
33511             }else{
33512                 row.className = row.className.replace("x-grid-row-alt", "");
33513             }
33514             if(lrow){
33515                 lrow.className = row.className;
33516             }
33517         }
33518     },
33519
33520     restoreScroll : function(state){
33521         var sb = this.scroller.dom;
33522         sb.scrollLeft = state.left;
33523         sb.scrollTop = state.top;
33524         this.syncScroll();
33525     },
33526
33527     syncScroll : function(){
33528         var sb = this.scroller.dom;
33529         var sh = this.mainHd.dom;
33530         var bs = this.mainBody.dom;
33531         var lv = this.lockedBody.dom;
33532         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33533         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33534     },
33535
33536     handleScroll : function(e){
33537         this.syncScroll();
33538         var sb = this.scroller.dom;
33539         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33540         e.stopEvent();
33541     },
33542
33543     handleWheel : function(e){
33544         var d = e.getWheelDelta();
33545         this.scroller.dom.scrollTop -= d*22;
33546         // set this here to prevent jumpy scrolling on large tables
33547         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33548         e.stopEvent();
33549     },
33550
33551     renderRows : function(startRow, endRow){
33552         // pull in all the crap needed to render rows
33553         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33554         var colCount = cm.getColumnCount();
33555
33556         if(ds.getCount() < 1){
33557             return ["", ""];
33558         }
33559
33560         // build a map for all the columns
33561         var cs = [];
33562         for(var i = 0; i < colCount; i++){
33563             var name = cm.getDataIndex(i);
33564             cs[i] = {
33565                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33566                 renderer : cm.getRenderer(i),
33567                 id : cm.getColumnId(i),
33568                 locked : cm.isLocked(i)
33569             };
33570         }
33571
33572         startRow = startRow || 0;
33573         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33574
33575         // records to render
33576         var rs = ds.getRange(startRow, endRow);
33577
33578         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33579     },
33580
33581     // As much as I hate to duplicate code, this was branched because FireFox really hates
33582     // [].join("") on strings. The performance difference was substantial enough to
33583     // branch this function
33584     doRender : Roo.isGecko ?
33585             function(cs, rs, ds, startRow, colCount, stripe){
33586                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33587                 // buffers
33588                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33589                 
33590                 var hasListener = this.grid.hasListener('rowclass');
33591                 var rowcfg = {};
33592                 for(var j = 0, len = rs.length; j < len; j++){
33593                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33594                     for(var i = 0; i < colCount; i++){
33595                         c = cs[i];
33596                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33597                         p.id = c.id;
33598                         p.css = p.attr = "";
33599                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33600                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33601                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33602                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33603                         }
33604                         var markup = ct.apply(p);
33605                         if(!c.locked){
33606                             cb+= markup;
33607                         }else{
33608                             lcb+= markup;
33609                         }
33610                     }
33611                     var alt = [];
33612                     if(stripe && ((rowIndex+1) % 2 == 0)){
33613                         alt.push("x-grid-row-alt")
33614                     }
33615                     if(r.dirty){
33616                         alt.push(  " x-grid-dirty-row");
33617                     }
33618                     rp.cells = lcb;
33619                     if(this.getRowClass){
33620                         alt.push(this.getRowClass(r, rowIndex));
33621                     }
33622                     if (hasListener) {
33623                         rowcfg = {
33624                              
33625                             record: r,
33626                             rowIndex : rowIndex,
33627                             rowClass : ''
33628                         }
33629                         this.grid.fireEvent('rowclass', this, rowcfg);
33630                         alt.push(rowcfg.rowClass);
33631                     }
33632                     rp.alt = alt.join(" ");
33633                     lbuf+= rt.apply(rp);
33634                     rp.cells = cb;
33635                     buf+=  rt.apply(rp);
33636                 }
33637                 return [lbuf, buf];
33638             } :
33639             function(cs, rs, ds, startRow, colCount, stripe){
33640                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33641                 // buffers
33642                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33643                 var hasListener = this.grid.hasListener('rowclass');
33644                 var rowcfg = {};
33645                 for(var j = 0, len = rs.length; j < len; j++){
33646                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33647                     for(var i = 0; i < colCount; i++){
33648                         c = cs[i];
33649                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33650                         p.id = c.id;
33651                         p.css = p.attr = "";
33652                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33653                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33654                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33655                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33656                         }
33657                         var markup = ct.apply(p);
33658                         if(!c.locked){
33659                             cb[cb.length] = markup;
33660                         }else{
33661                             lcb[lcb.length] = markup;
33662                         }
33663                     }
33664                     var alt = [];
33665                     if(stripe && ((rowIndex+1) % 2 == 0)){
33666                         alt.push( "x-grid-row-alt");
33667                     }
33668                     if(r.dirty){
33669                         alt.push(" x-grid-dirty-row");
33670                     }
33671                     rp.cells = lcb;
33672                     if(this.getRowClass){
33673                         alt.push( this.getRowClass(r, rowIndex));
33674                     }
33675                     if (hasListener) {
33676                         rowcfg = {
33677                              
33678                             record: r,
33679                             rowIndex : rowIndex,
33680                             rowClass : ''
33681                         }
33682                         this.grid.fireEvent('rowclass', this, rowcfg);
33683                         alt.push(rowcfg.rowClass);
33684                     }
33685                     rp.alt = alt.join(" ");
33686                     rp.cells = lcb.join("");
33687                     lbuf[lbuf.length] = rt.apply(rp);
33688                     rp.cells = cb.join("");
33689                     buf[buf.length] =  rt.apply(rp);
33690                 }
33691                 return [lbuf.join(""), buf.join("")];
33692             },
33693
33694     renderBody : function(){
33695         var markup = this.renderRows();
33696         var bt = this.templates.body;
33697         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33698     },
33699
33700     /**
33701      * Refreshes the grid
33702      * @param {Boolean} headersToo
33703      */
33704     refresh : function(headersToo){
33705         this.fireEvent("beforerefresh", this);
33706         this.grid.stopEditing();
33707         var result = this.renderBody();
33708         this.lockedBody.update(result[0]);
33709         this.mainBody.update(result[1]);
33710         if(headersToo === true){
33711             this.updateHeaders();
33712             this.updateColumns();
33713             this.updateSplitters();
33714             this.updateHeaderSortState();
33715         }
33716         this.syncRowHeights();
33717         this.layout();
33718         this.fireEvent("refresh", this);
33719     },
33720
33721     handleColumnMove : function(cm, oldIndex, newIndex){
33722         this.indexMap = null;
33723         var s = this.getScrollState();
33724         this.refresh(true);
33725         this.restoreScroll(s);
33726         this.afterMove(newIndex);
33727     },
33728
33729     afterMove : function(colIndex){
33730         if(this.enableMoveAnim && Roo.enableFx){
33731             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33732         }
33733     },
33734
33735     updateCell : function(dm, rowIndex, dataIndex){
33736         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33737         if(typeof colIndex == "undefined"){ // not present in grid
33738             return;
33739         }
33740         var cm = this.grid.colModel;
33741         var cell = this.getCell(rowIndex, colIndex);
33742         var cellText = this.getCellText(rowIndex, colIndex);
33743
33744         var p = {
33745             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33746             id : cm.getColumnId(colIndex),
33747             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33748         };
33749         var renderer = cm.getRenderer(colIndex);
33750         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33751         if(typeof val == "undefined" || val === "") val = "&#160;";
33752         cellText.innerHTML = val;
33753         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33754         this.syncRowHeights(rowIndex, rowIndex);
33755     },
33756
33757     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33758         var maxWidth = 0;
33759         if(this.grid.autoSizeHeaders){
33760             var h = this.getHeaderCellMeasure(colIndex);
33761             maxWidth = Math.max(maxWidth, h.scrollWidth);
33762         }
33763         var tb, index;
33764         if(this.cm.isLocked(colIndex)){
33765             tb = this.getLockedTable();
33766             index = colIndex;
33767         }else{
33768             tb = this.getBodyTable();
33769             index = colIndex - this.cm.getLockedCount();
33770         }
33771         if(tb && tb.rows){
33772             var rows = tb.rows;
33773             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33774             for(var i = 0; i < stopIndex; i++){
33775                 var cell = rows[i].childNodes[index].firstChild;
33776                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33777             }
33778         }
33779         return maxWidth + /*margin for error in IE*/ 5;
33780     },
33781     /**
33782      * Autofit a column to its content.
33783      * @param {Number} colIndex
33784      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33785      */
33786      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33787          if(this.cm.isHidden(colIndex)){
33788              return; // can't calc a hidden column
33789          }
33790         if(forceMinSize){
33791             var cid = this.cm.getColumnId(colIndex);
33792             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33793            if(this.grid.autoSizeHeaders){
33794                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33795            }
33796         }
33797         var newWidth = this.calcColumnWidth(colIndex);
33798         this.cm.setColumnWidth(colIndex,
33799             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33800         if(!suppressEvent){
33801             this.grid.fireEvent("columnresize", colIndex, newWidth);
33802         }
33803     },
33804
33805     /**
33806      * Autofits all columns to their content and then expands to fit any extra space in the grid
33807      */
33808      autoSizeColumns : function(){
33809         var cm = this.grid.colModel;
33810         var colCount = cm.getColumnCount();
33811         for(var i = 0; i < colCount; i++){
33812             this.autoSizeColumn(i, true, true);
33813         }
33814         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33815             this.fitColumns();
33816         }else{
33817             this.updateColumns();
33818             this.layout();
33819         }
33820     },
33821
33822     /**
33823      * Autofits all columns to the grid's width proportionate with their current size
33824      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33825      */
33826     fitColumns : function(reserveScrollSpace){
33827         var cm = this.grid.colModel;
33828         var colCount = cm.getColumnCount();
33829         var cols = [];
33830         var width = 0;
33831         var i, w;
33832         for (i = 0; i < colCount; i++){
33833             if(!cm.isHidden(i) && !cm.isFixed(i)){
33834                 w = cm.getColumnWidth(i);
33835                 cols.push(i);
33836                 cols.push(w);
33837                 width += w;
33838             }
33839         }
33840         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33841         if(reserveScrollSpace){
33842             avail -= 17;
33843         }
33844         var frac = (avail - cm.getTotalWidth())/width;
33845         while (cols.length){
33846             w = cols.pop();
33847             i = cols.pop();
33848             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33849         }
33850         this.updateColumns();
33851         this.layout();
33852     },
33853
33854     onRowSelect : function(rowIndex){
33855         var row = this.getRowComposite(rowIndex);
33856         row.addClass("x-grid-row-selected");
33857     },
33858
33859     onRowDeselect : function(rowIndex){
33860         var row = this.getRowComposite(rowIndex);
33861         row.removeClass("x-grid-row-selected");
33862     },
33863
33864     onCellSelect : function(row, col){
33865         var cell = this.getCell(row, col);
33866         if(cell){
33867             Roo.fly(cell).addClass("x-grid-cell-selected");
33868         }
33869     },
33870
33871     onCellDeselect : function(row, col){
33872         var cell = this.getCell(row, col);
33873         if(cell){
33874             Roo.fly(cell).removeClass("x-grid-cell-selected");
33875         }
33876     },
33877
33878     updateHeaderSortState : function(){
33879         var state = this.ds.getSortState();
33880         if(!state){
33881             return;
33882         }
33883         this.sortState = state;
33884         var sortColumn = this.cm.findColumnIndex(state.field);
33885         if(sortColumn != -1){
33886             var sortDir = state.direction;
33887             var sc = this.sortClasses;
33888             var hds = this.el.select(this.headerSelector).removeClass(sc);
33889             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33890         }
33891     },
33892
33893     handleHeaderClick : function(g, index){
33894         if(this.headersDisabled){
33895             return;
33896         }
33897         var dm = g.dataSource, cm = g.colModel;
33898             if(!cm.isSortable(index)){
33899             return;
33900         }
33901             g.stopEditing();
33902         dm.sort(cm.getDataIndex(index));
33903     },
33904
33905
33906     destroy : function(){
33907         if(this.colMenu){
33908             this.colMenu.removeAll();
33909             Roo.menu.MenuMgr.unregister(this.colMenu);
33910             this.colMenu.getEl().remove();
33911             delete this.colMenu;
33912         }
33913         if(this.hmenu){
33914             this.hmenu.removeAll();
33915             Roo.menu.MenuMgr.unregister(this.hmenu);
33916             this.hmenu.getEl().remove();
33917             delete this.hmenu;
33918         }
33919         if(this.grid.enableColumnMove){
33920             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33921             if(dds){
33922                 for(var dd in dds){
33923                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33924                         var elid = dds[dd].dragElId;
33925                         dds[dd].unreg();
33926                         Roo.get(elid).remove();
33927                     } else if(dds[dd].config.isTarget){
33928                         dds[dd].proxyTop.remove();
33929                         dds[dd].proxyBottom.remove();
33930                         dds[dd].unreg();
33931                     }
33932                     if(Roo.dd.DDM.locationCache[dd]){
33933                         delete Roo.dd.DDM.locationCache[dd];
33934                     }
33935                 }
33936                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33937             }
33938         }
33939         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33940         this.bind(null, null);
33941         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33942     },
33943
33944     handleLockChange : function(){
33945         this.refresh(true);
33946     },
33947
33948     onDenyColumnLock : function(){
33949
33950     },
33951
33952     onDenyColumnHide : function(){
33953
33954     },
33955
33956     handleHdMenuClick : function(item){
33957         var index = this.hdCtxIndex;
33958         var cm = this.cm, ds = this.ds;
33959         switch(item.id){
33960             case "asc":
33961                 ds.sort(cm.getDataIndex(index), "ASC");
33962                 break;
33963             case "desc":
33964                 ds.sort(cm.getDataIndex(index), "DESC");
33965                 break;
33966             case "lock":
33967                 var lc = cm.getLockedCount();
33968                 if(cm.getColumnCount(true) <= lc+1){
33969                     this.onDenyColumnLock();
33970                     return;
33971                 }
33972                 if(lc != index){
33973                     cm.setLocked(index, true, true);
33974                     cm.moveColumn(index, lc);
33975                     this.grid.fireEvent("columnmove", index, lc);
33976                 }else{
33977                     cm.setLocked(index, true);
33978                 }
33979             break;
33980             case "unlock":
33981                 var lc = cm.getLockedCount();
33982                 if((lc-1) != index){
33983                     cm.setLocked(index, false, true);
33984                     cm.moveColumn(index, lc-1);
33985                     this.grid.fireEvent("columnmove", index, lc-1);
33986                 }else{
33987                     cm.setLocked(index, false);
33988                 }
33989             break;
33990             default:
33991                 index = cm.getIndexById(item.id.substr(4));
33992                 if(index != -1){
33993                     if(item.checked && cm.getColumnCount(true) <= 1){
33994                         this.onDenyColumnHide();
33995                         return false;
33996                     }
33997                     cm.setHidden(index, item.checked);
33998                 }
33999         }
34000         return true;
34001     },
34002
34003     beforeColMenuShow : function(){
34004         var cm = this.cm,  colCount = cm.getColumnCount();
34005         this.colMenu.removeAll();
34006         for(var i = 0; i < colCount; i++){
34007             this.colMenu.add(new Roo.menu.CheckItem({
34008                 id: "col-"+cm.getColumnId(i),
34009                 text: cm.getColumnHeader(i),
34010                 checked: !cm.isHidden(i),
34011                 hideOnClick:false
34012             }));
34013         }
34014     },
34015
34016     handleHdCtx : function(g, index, e){
34017         e.stopEvent();
34018         var hd = this.getHeaderCell(index);
34019         this.hdCtxIndex = index;
34020         var ms = this.hmenu.items, cm = this.cm;
34021         ms.get("asc").setDisabled(!cm.isSortable(index));
34022         ms.get("desc").setDisabled(!cm.isSortable(index));
34023         if(this.grid.enableColLock !== false){
34024             ms.get("lock").setDisabled(cm.isLocked(index));
34025             ms.get("unlock").setDisabled(!cm.isLocked(index));
34026         }
34027         this.hmenu.show(hd, "tl-bl");
34028     },
34029
34030     handleHdOver : function(e){
34031         var hd = this.findHeaderCell(e.getTarget());
34032         if(hd && !this.headersDisabled){
34033             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34034                this.fly(hd).addClass("x-grid-hd-over");
34035             }
34036         }
34037     },
34038
34039     handleHdOut : function(e){
34040         var hd = this.findHeaderCell(e.getTarget());
34041         if(hd){
34042             this.fly(hd).removeClass("x-grid-hd-over");
34043         }
34044     },
34045
34046     handleSplitDblClick : function(e, t){
34047         var i = this.getCellIndex(t);
34048         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34049             this.autoSizeColumn(i, true);
34050             this.layout();
34051         }
34052     },
34053
34054     render : function(){
34055
34056         var cm = this.cm;
34057         var colCount = cm.getColumnCount();
34058
34059         if(this.grid.monitorWindowResize === true){
34060             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34061         }
34062         var header = this.renderHeaders();
34063         var body = this.templates.body.apply({rows:""});
34064         var html = this.templates.master.apply({
34065             lockedBody: body,
34066             body: body,
34067             lockedHeader: header[0],
34068             header: header[1]
34069         });
34070
34071         //this.updateColumns();
34072
34073         this.grid.getGridEl().dom.innerHTML = html;
34074
34075         this.initElements();
34076         
34077         // a kludge to fix the random scolling effect in webkit
34078         this.el.on("scroll", function() {
34079             this.el.dom.scrollTop=0; // hopefully not recursive..
34080         },this);
34081
34082         this.scroller.on("scroll", this.handleScroll, this);
34083         this.lockedBody.on("mousewheel", this.handleWheel, this);
34084         this.mainBody.on("mousewheel", this.handleWheel, this);
34085
34086         this.mainHd.on("mouseover", this.handleHdOver, this);
34087         this.mainHd.on("mouseout", this.handleHdOut, this);
34088         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34089                 {delegate: "."+this.splitClass});
34090
34091         this.lockedHd.on("mouseover", this.handleHdOver, this);
34092         this.lockedHd.on("mouseout", this.handleHdOut, this);
34093         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34094                 {delegate: "."+this.splitClass});
34095
34096         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34097             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34098         }
34099
34100         this.updateSplitters();
34101
34102         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34103             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34104             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34105         }
34106
34107         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34108             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34109             this.hmenu.add(
34110                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34111                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34112             );
34113             if(this.grid.enableColLock !== false){
34114                 this.hmenu.add('-',
34115                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34116                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34117                 );
34118             }
34119             if(this.grid.enableColumnHide !== false){
34120
34121                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34122                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34123                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34124
34125                 this.hmenu.add('-',
34126                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34127                 );
34128             }
34129             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34130
34131             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34132         }
34133
34134         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34135             this.dd = new Roo.grid.GridDragZone(this.grid, {
34136                 ddGroup : this.grid.ddGroup || 'GridDD'
34137             });
34138         }
34139
34140         /*
34141         for(var i = 0; i < colCount; i++){
34142             if(cm.isHidden(i)){
34143                 this.hideColumn(i);
34144             }
34145             if(cm.config[i].align){
34146                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34147                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34148             }
34149         }*/
34150         
34151         this.updateHeaderSortState();
34152
34153         this.beforeInitialResize();
34154         this.layout(true);
34155
34156         // two part rendering gives faster view to the user
34157         this.renderPhase2.defer(1, this);
34158     },
34159
34160     renderPhase2 : function(){
34161         // render the rows now
34162         this.refresh();
34163         if(this.grid.autoSizeColumns){
34164             this.autoSizeColumns();
34165         }
34166     },
34167
34168     beforeInitialResize : function(){
34169
34170     },
34171
34172     onColumnSplitterMoved : function(i, w){
34173         this.userResized = true;
34174         var cm = this.grid.colModel;
34175         cm.setColumnWidth(i, w, true);
34176         var cid = cm.getColumnId(i);
34177         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34178         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34179         this.updateSplitters();
34180         this.layout();
34181         this.grid.fireEvent("columnresize", i, w);
34182     },
34183
34184     syncRowHeights : function(startIndex, endIndex){
34185         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34186             startIndex = startIndex || 0;
34187             var mrows = this.getBodyTable().rows;
34188             var lrows = this.getLockedTable().rows;
34189             var len = mrows.length-1;
34190             endIndex = Math.min(endIndex || len, len);
34191             for(var i = startIndex; i <= endIndex; i++){
34192                 var m = mrows[i], l = lrows[i];
34193                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34194                 m.style.height = l.style.height = h + "px";
34195             }
34196         }
34197     },
34198
34199     layout : function(initialRender, is2ndPass){
34200         var g = this.grid;
34201         var auto = g.autoHeight;
34202         var scrollOffset = 16;
34203         var c = g.getGridEl(), cm = this.cm,
34204                 expandCol = g.autoExpandColumn,
34205                 gv = this;
34206         //c.beginMeasure();
34207
34208         if(!c.dom.offsetWidth){ // display:none?
34209             if(initialRender){
34210                 this.lockedWrap.show();
34211                 this.mainWrap.show();
34212             }
34213             return;
34214         }
34215
34216         var hasLock = this.cm.isLocked(0);
34217
34218         var tbh = this.headerPanel.getHeight();
34219         var bbh = this.footerPanel.getHeight();
34220
34221         if(auto){
34222             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34223             var newHeight = ch + c.getBorderWidth("tb");
34224             if(g.maxHeight){
34225                 newHeight = Math.min(g.maxHeight, newHeight);
34226             }
34227             c.setHeight(newHeight);
34228         }
34229
34230         if(g.autoWidth){
34231             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34232         }
34233
34234         var s = this.scroller;
34235
34236         var csize = c.getSize(true);
34237
34238         this.el.setSize(csize.width, csize.height);
34239
34240         this.headerPanel.setWidth(csize.width);
34241         this.footerPanel.setWidth(csize.width);
34242
34243         var hdHeight = this.mainHd.getHeight();
34244         var vw = csize.width;
34245         var vh = csize.height - (tbh + bbh);
34246
34247         s.setSize(vw, vh);
34248
34249         var bt = this.getBodyTable();
34250         var ltWidth = hasLock ?
34251                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34252
34253         var scrollHeight = bt.offsetHeight;
34254         var scrollWidth = ltWidth + bt.offsetWidth;
34255         var vscroll = false, hscroll = false;
34256
34257         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34258
34259         var lw = this.lockedWrap, mw = this.mainWrap;
34260         var lb = this.lockedBody, mb = this.mainBody;
34261
34262         setTimeout(function(){
34263             var t = s.dom.offsetTop;
34264             var w = s.dom.clientWidth,
34265                 h = s.dom.clientHeight;
34266
34267             lw.setTop(t);
34268             lw.setSize(ltWidth, h);
34269
34270             mw.setLeftTop(ltWidth, t);
34271             mw.setSize(w-ltWidth, h);
34272
34273             lb.setHeight(h-hdHeight);
34274             mb.setHeight(h-hdHeight);
34275
34276             if(is2ndPass !== true && !gv.userResized && expandCol){
34277                 // high speed resize without full column calculation
34278                 
34279                 var ci = cm.getIndexById(expandCol);
34280                 if (ci < 0) {
34281                     ci = cm.findColumnIndex(expandCol);
34282                 }
34283                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34284                 var expandId = cm.getColumnId(ci);
34285                 var  tw = cm.getTotalWidth(false);
34286                 var currentWidth = cm.getColumnWidth(ci);
34287                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34288                 if(currentWidth != cw){
34289                     cm.setColumnWidth(ci, cw, true);
34290                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34291                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34292                     gv.updateSplitters();
34293                     gv.layout(false, true);
34294                 }
34295             }
34296
34297             if(initialRender){
34298                 lw.show();
34299                 mw.show();
34300             }
34301             //c.endMeasure();
34302         }, 10);
34303     },
34304
34305     onWindowResize : function(){
34306         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34307             return;
34308         }
34309         this.layout();
34310     },
34311
34312     appendFooter : function(parentEl){
34313         return null;
34314     },
34315
34316     sortAscText : "Sort Ascending",
34317     sortDescText : "Sort Descending",
34318     lockText : "Lock Column",
34319     unlockText : "Unlock Column",
34320     columnsText : "Columns"
34321 });
34322
34323
34324 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34325     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34326     this.proxy.el.addClass('x-grid3-col-dd');
34327 };
34328
34329 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34330     handleMouseDown : function(e){
34331
34332     },
34333
34334     callHandleMouseDown : function(e){
34335         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34336     }
34337 });
34338 /*
34339  * Based on:
34340  * Ext JS Library 1.1.1
34341  * Copyright(c) 2006-2007, Ext JS, LLC.
34342  *
34343  * Originally Released Under LGPL - original licence link has changed is not relivant.
34344  *
34345  * Fork - LGPL
34346  * <script type="text/javascript">
34347  */
34348  
34349 // private
34350 // This is a support class used internally by the Grid components
34351 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34352     this.grid = grid;
34353     this.view = grid.getView();
34354     this.proxy = this.view.resizeProxy;
34355     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34356         "gridSplitters" + this.grid.getGridEl().id, {
34357         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34358     });
34359     this.setHandleElId(Roo.id(hd));
34360     this.setOuterHandleElId(Roo.id(hd2));
34361     this.scroll = false;
34362 };
34363 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34364     fly: Roo.Element.fly,
34365
34366     b4StartDrag : function(x, y){
34367         this.view.headersDisabled = true;
34368         this.proxy.setHeight(this.view.mainWrap.getHeight());
34369         var w = this.cm.getColumnWidth(this.cellIndex);
34370         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34371         this.resetConstraints();
34372         this.setXConstraint(minw, 1000);
34373         this.setYConstraint(0, 0);
34374         this.minX = x - minw;
34375         this.maxX = x + 1000;
34376         this.startPos = x;
34377         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34378     },
34379
34380
34381     handleMouseDown : function(e){
34382         ev = Roo.EventObject.setEvent(e);
34383         var t = this.fly(ev.getTarget());
34384         if(t.hasClass("x-grid-split")){
34385             this.cellIndex = this.view.getCellIndex(t.dom);
34386             this.split = t.dom;
34387             this.cm = this.grid.colModel;
34388             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34389                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34390             }
34391         }
34392     },
34393
34394     endDrag : function(e){
34395         this.view.headersDisabled = false;
34396         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34397         var diff = endX - this.startPos;
34398         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34399     },
34400
34401     autoOffset : function(){
34402         this.setDelta(0,0);
34403     }
34404 });/*
34405  * Based on:
34406  * Ext JS Library 1.1.1
34407  * Copyright(c) 2006-2007, Ext JS, LLC.
34408  *
34409  * Originally Released Under LGPL - original licence link has changed is not relivant.
34410  *
34411  * Fork - LGPL
34412  * <script type="text/javascript">
34413  */
34414  
34415 // private
34416 // This is a support class used internally by the Grid components
34417 Roo.grid.GridDragZone = function(grid, config){
34418     this.view = grid.getView();
34419     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34420     if(this.view.lockedBody){
34421         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34422         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34423     }
34424     this.scroll = false;
34425     this.grid = grid;
34426     this.ddel = document.createElement('div');
34427     this.ddel.className = 'x-grid-dd-wrap';
34428 };
34429
34430 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34431     ddGroup : "GridDD",
34432
34433     getDragData : function(e){
34434         var t = Roo.lib.Event.getTarget(e);
34435         var rowIndex = this.view.findRowIndex(t);
34436         if(rowIndex !== false){
34437             var sm = this.grid.selModel;
34438             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34439               //  sm.mouseDown(e, t);
34440             //}
34441             if (e.hasModifier()){
34442                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34443             }
34444             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34445         }
34446         return false;
34447     },
34448
34449     onInitDrag : function(e){
34450         var data = this.dragData;
34451         this.ddel.innerHTML = this.grid.getDragDropText();
34452         this.proxy.update(this.ddel);
34453         // fire start drag?
34454     },
34455
34456     afterRepair : function(){
34457         this.dragging = false;
34458     },
34459
34460     getRepairXY : function(e, data){
34461         return false;
34462     },
34463
34464     onEndDrag : function(data, e){
34465         // fire end drag?
34466     },
34467
34468     onValidDrop : function(dd, e, id){
34469         // fire drag drop?
34470         this.hideProxy();
34471     },
34472
34473     beforeInvalidDrop : function(e, id){
34474
34475     }
34476 });/*
34477  * Based on:
34478  * Ext JS Library 1.1.1
34479  * Copyright(c) 2006-2007, Ext JS, LLC.
34480  *
34481  * Originally Released Under LGPL - original licence link has changed is not relivant.
34482  *
34483  * Fork - LGPL
34484  * <script type="text/javascript">
34485  */
34486  
34487
34488 /**
34489  * @class Roo.grid.ColumnModel
34490  * @extends Roo.util.Observable
34491  * This is the default implementation of a ColumnModel used by the Grid. It defines
34492  * the columns in the grid.
34493  * <br>Usage:<br>
34494  <pre><code>
34495  var colModel = new Roo.grid.ColumnModel([
34496         {header: "Ticker", width: 60, sortable: true, locked: true},
34497         {header: "Company Name", width: 150, sortable: true},
34498         {header: "Market Cap.", width: 100, sortable: true},
34499         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34500         {header: "Employees", width: 100, sortable: true, resizable: false}
34501  ]);
34502  </code></pre>
34503  * <p>
34504  
34505  * The config options listed for this class are options which may appear in each
34506  * individual column definition.
34507  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34508  * @constructor
34509  * @param {Object} config An Array of column config objects. See this class's
34510  * config objects for details.
34511 */
34512 Roo.grid.ColumnModel = function(config){
34513         /**
34514      * The config passed into the constructor
34515      */
34516     this.config = config;
34517     this.lookup = {};
34518
34519     // if no id, create one
34520     // if the column does not have a dataIndex mapping,
34521     // map it to the order it is in the config
34522     for(var i = 0, len = config.length; i < len; i++){
34523         var c = config[i];
34524         if(typeof c.dataIndex == "undefined"){
34525             c.dataIndex = i;
34526         }
34527         if(typeof c.renderer == "string"){
34528             c.renderer = Roo.util.Format[c.renderer];
34529         }
34530         if(typeof c.id == "undefined"){
34531             c.id = Roo.id();
34532         }
34533         if(c.editor && c.editor.xtype){
34534             c.editor  = Roo.factory(c.editor, Roo.grid);
34535         }
34536         if(c.editor && c.editor.isFormField){
34537             c.editor = new Roo.grid.GridEditor(c.editor);
34538         }
34539         this.lookup[c.id] = c;
34540     }
34541
34542     /**
34543      * The width of columns which have no width specified (defaults to 100)
34544      * @type Number
34545      */
34546     this.defaultWidth = 100;
34547
34548     /**
34549      * Default sortable of columns which have no sortable specified (defaults to false)
34550      * @type Boolean
34551      */
34552     this.defaultSortable = false;
34553
34554     this.addEvents({
34555         /**
34556              * @event widthchange
34557              * Fires when the width of a column changes.
34558              * @param {ColumnModel} this
34559              * @param {Number} columnIndex The column index
34560              * @param {Number} newWidth The new width
34561              */
34562             "widthchange": true,
34563         /**
34564              * @event headerchange
34565              * Fires when the text of a header changes.
34566              * @param {ColumnModel} this
34567              * @param {Number} columnIndex The column index
34568              * @param {Number} newText The new header text
34569              */
34570             "headerchange": true,
34571         /**
34572              * @event hiddenchange
34573              * Fires when a column is hidden or "unhidden".
34574              * @param {ColumnModel} this
34575              * @param {Number} columnIndex The column index
34576              * @param {Boolean} hidden true if hidden, false otherwise
34577              */
34578             "hiddenchange": true,
34579             /**
34580          * @event columnmoved
34581          * Fires when a column is moved.
34582          * @param {ColumnModel} this
34583          * @param {Number} oldIndex
34584          * @param {Number} newIndex
34585          */
34586         "columnmoved" : true,
34587         /**
34588          * @event columlockchange
34589          * Fires when a column's locked state is changed
34590          * @param {ColumnModel} this
34591          * @param {Number} colIndex
34592          * @param {Boolean} locked true if locked
34593          */
34594         "columnlockchange" : true
34595     });
34596     Roo.grid.ColumnModel.superclass.constructor.call(this);
34597 };
34598 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34599     /**
34600      * @cfg {String} header The header text to display in the Grid view.
34601      */
34602     /**
34603      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34604      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34605      * specified, the column's index is used as an index into the Record's data Array.
34606      */
34607     /**
34608      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34609      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34610      */
34611     /**
34612      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34613      * Defaults to the value of the {@link #defaultSortable} property.
34614      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34615      */
34616     /**
34617      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34618      */
34619     /**
34620      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34621      */
34622     /**
34623      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34624      */
34625     /**
34626      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34627      */
34628     /**
34629      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34630      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34631      * default renderer uses the raw data value.
34632      */
34633        /**
34634      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34635      */
34636     /**
34637      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34638      */
34639
34640     /**
34641      * Returns the id of the column at the specified index.
34642      * @param {Number} index The column index
34643      * @return {String} the id
34644      */
34645     getColumnId : function(index){
34646         return this.config[index].id;
34647     },
34648
34649     /**
34650      * Returns the column for a specified id.
34651      * @param {String} id The column id
34652      * @return {Object} the column
34653      */
34654     getColumnById : function(id){
34655         return this.lookup[id];
34656     },
34657
34658     
34659     /**
34660      * Returns the column for a specified dataIndex.
34661      * @param {String} dataIndex The column dataIndex
34662      * @return {Object|Boolean} the column or false if not found
34663      */
34664     getColumnByDataIndex: function(dataIndex){
34665         var index = this.findColumnIndex(dataIndex);
34666         return index > -1 ? this.config[index] : false;
34667     },
34668     
34669     /**
34670      * Returns the index for a specified column id.
34671      * @param {String} id The column id
34672      * @return {Number} the index, or -1 if not found
34673      */
34674     getIndexById : function(id){
34675         for(var i = 0, len = this.config.length; i < len; i++){
34676             if(this.config[i].id == id){
34677                 return i;
34678             }
34679         }
34680         return -1;
34681     },
34682     
34683     /**
34684      * Returns the index for a specified column dataIndex.
34685      * @param {String} dataIndex The column dataIndex
34686      * @return {Number} the index, or -1 if not found
34687      */
34688     
34689     findColumnIndex : function(dataIndex){
34690         for(var i = 0, len = this.config.length; i < len; i++){
34691             if(this.config[i].dataIndex == dataIndex){
34692                 return i;
34693             }
34694         }
34695         return -1;
34696     },
34697     
34698     
34699     moveColumn : function(oldIndex, newIndex){
34700         var c = this.config[oldIndex];
34701         this.config.splice(oldIndex, 1);
34702         this.config.splice(newIndex, 0, c);
34703         this.dataMap = null;
34704         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34705     },
34706
34707     isLocked : function(colIndex){
34708         return this.config[colIndex].locked === true;
34709     },
34710
34711     setLocked : function(colIndex, value, suppressEvent){
34712         if(this.isLocked(colIndex) == value){
34713             return;
34714         }
34715         this.config[colIndex].locked = value;
34716         if(!suppressEvent){
34717             this.fireEvent("columnlockchange", this, colIndex, value);
34718         }
34719     },
34720
34721     getTotalLockedWidth : function(){
34722         var totalWidth = 0;
34723         for(var i = 0; i < this.config.length; i++){
34724             if(this.isLocked(i) && !this.isHidden(i)){
34725                 this.totalWidth += this.getColumnWidth(i);
34726             }
34727         }
34728         return totalWidth;
34729     },
34730
34731     getLockedCount : function(){
34732         for(var i = 0, len = this.config.length; i < len; i++){
34733             if(!this.isLocked(i)){
34734                 return i;
34735             }
34736         }
34737     },
34738
34739     /**
34740      * Returns the number of columns.
34741      * @return {Number}
34742      */
34743     getColumnCount : function(visibleOnly){
34744         if(visibleOnly === true){
34745             var c = 0;
34746             for(var i = 0, len = this.config.length; i < len; i++){
34747                 if(!this.isHidden(i)){
34748                     c++;
34749                 }
34750             }
34751             return c;
34752         }
34753         return this.config.length;
34754     },
34755
34756     /**
34757      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34758      * @param {Function} fn
34759      * @param {Object} scope (optional)
34760      * @return {Array} result
34761      */
34762     getColumnsBy : function(fn, scope){
34763         var r = [];
34764         for(var i = 0, len = this.config.length; i < len; i++){
34765             var c = this.config[i];
34766             if(fn.call(scope||this, c, i) === true){
34767                 r[r.length] = c;
34768             }
34769         }
34770         return r;
34771     },
34772
34773     /**
34774      * Returns true if the specified column is sortable.
34775      * @param {Number} col The column index
34776      * @return {Boolean}
34777      */
34778     isSortable : function(col){
34779         if(typeof this.config[col].sortable == "undefined"){
34780             return this.defaultSortable;
34781         }
34782         return this.config[col].sortable;
34783     },
34784
34785     /**
34786      * Returns the rendering (formatting) function defined for the column.
34787      * @param {Number} col The column index.
34788      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34789      */
34790     getRenderer : function(col){
34791         if(!this.config[col].renderer){
34792             return Roo.grid.ColumnModel.defaultRenderer;
34793         }
34794         return this.config[col].renderer;
34795     },
34796
34797     /**
34798      * Sets the rendering (formatting) function for a column.
34799      * @param {Number} col The column index
34800      * @param {Function} fn The function to use to process the cell's raw data
34801      * to return HTML markup for the grid view. The render function is called with
34802      * the following parameters:<ul>
34803      * <li>Data value.</li>
34804      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34805      * <li>css A CSS style string to apply to the table cell.</li>
34806      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34807      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34808      * <li>Row index</li>
34809      * <li>Column index</li>
34810      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34811      */
34812     setRenderer : function(col, fn){
34813         this.config[col].renderer = fn;
34814     },
34815
34816     /**
34817      * Returns the width for the specified column.
34818      * @param {Number} col The column index
34819      * @return {Number}
34820      */
34821     getColumnWidth : function(col){
34822         return this.config[col].width || this.defaultWidth;
34823     },
34824
34825     /**
34826      * Sets the width for a column.
34827      * @param {Number} col The column index
34828      * @param {Number} width The new width
34829      */
34830     setColumnWidth : function(col, width, suppressEvent){
34831         this.config[col].width = width;
34832         this.totalWidth = null;
34833         if(!suppressEvent){
34834              this.fireEvent("widthchange", this, col, width);
34835         }
34836     },
34837
34838     /**
34839      * Returns the total width of all columns.
34840      * @param {Boolean} includeHidden True to include hidden column widths
34841      * @return {Number}
34842      */
34843     getTotalWidth : function(includeHidden){
34844         if(!this.totalWidth){
34845             this.totalWidth = 0;
34846             for(var i = 0, len = this.config.length; i < len; i++){
34847                 if(includeHidden || !this.isHidden(i)){
34848                     this.totalWidth += this.getColumnWidth(i);
34849                 }
34850             }
34851         }
34852         return this.totalWidth;
34853     },
34854
34855     /**
34856      * Returns the header for the specified column.
34857      * @param {Number} col The column index
34858      * @return {String}
34859      */
34860     getColumnHeader : function(col){
34861         return this.config[col].header;
34862     },
34863
34864     /**
34865      * Sets the header for a column.
34866      * @param {Number} col The column index
34867      * @param {String} header The new header
34868      */
34869     setColumnHeader : function(col, header){
34870         this.config[col].header = header;
34871         this.fireEvent("headerchange", this, col, header);
34872     },
34873
34874     /**
34875      * Returns the tooltip for the specified column.
34876      * @param {Number} col The column index
34877      * @return {String}
34878      */
34879     getColumnTooltip : function(col){
34880             return this.config[col].tooltip;
34881     },
34882     /**
34883      * Sets the tooltip for a column.
34884      * @param {Number} col The column index
34885      * @param {String} tooltip The new tooltip
34886      */
34887     setColumnTooltip : function(col, tooltip){
34888             this.config[col].tooltip = tooltip;
34889     },
34890
34891     /**
34892      * Returns the dataIndex for the specified column.
34893      * @param {Number} col The column index
34894      * @return {Number}
34895      */
34896     getDataIndex : function(col){
34897         return this.config[col].dataIndex;
34898     },
34899
34900     /**
34901      * Sets the dataIndex for a column.
34902      * @param {Number} col The column index
34903      * @param {Number} dataIndex The new dataIndex
34904      */
34905     setDataIndex : function(col, dataIndex){
34906         this.config[col].dataIndex = dataIndex;
34907     },
34908
34909     
34910     
34911     /**
34912      * Returns true if the cell is editable.
34913      * @param {Number} colIndex The column index
34914      * @param {Number} rowIndex The row index
34915      * @return {Boolean}
34916      */
34917     isCellEditable : function(colIndex, rowIndex){
34918         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34919     },
34920
34921     /**
34922      * Returns the editor defined for the cell/column.
34923      * return false or null to disable editing.
34924      * @param {Number} colIndex The column index
34925      * @param {Number} rowIndex The row index
34926      * @return {Object}
34927      */
34928     getCellEditor : function(colIndex, rowIndex){
34929         return this.config[colIndex].editor;
34930     },
34931
34932     /**
34933      * Sets if a column is editable.
34934      * @param {Number} col The column index
34935      * @param {Boolean} editable True if the column is editable
34936      */
34937     setEditable : function(col, editable){
34938         this.config[col].editable = editable;
34939     },
34940
34941
34942     /**
34943      * Returns true if the column is hidden.
34944      * @param {Number} colIndex The column index
34945      * @return {Boolean}
34946      */
34947     isHidden : function(colIndex){
34948         return this.config[colIndex].hidden;
34949     },
34950
34951
34952     /**
34953      * Returns true if the column width cannot be changed
34954      */
34955     isFixed : function(colIndex){
34956         return this.config[colIndex].fixed;
34957     },
34958
34959     /**
34960      * Returns true if the column can be resized
34961      * @return {Boolean}
34962      */
34963     isResizable : function(colIndex){
34964         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34965     },
34966     /**
34967      * Sets if a column is hidden.
34968      * @param {Number} colIndex The column index
34969      * @param {Boolean} hidden True if the column is hidden
34970      */
34971     setHidden : function(colIndex, hidden){
34972         this.config[colIndex].hidden = hidden;
34973         this.totalWidth = null;
34974         this.fireEvent("hiddenchange", this, colIndex, hidden);
34975     },
34976
34977     /**
34978      * Sets the editor for a column.
34979      * @param {Number} col The column index
34980      * @param {Object} editor The editor object
34981      */
34982     setEditor : function(col, editor){
34983         this.config[col].editor = editor;
34984     }
34985 });
34986
34987 Roo.grid.ColumnModel.defaultRenderer = function(value){
34988         if(typeof value == "string" && value.length < 1){
34989             return "&#160;";
34990         }
34991         return value;
34992 };
34993
34994 // Alias for backwards compatibility
34995 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34996 /*
34997  * Based on:
34998  * Ext JS Library 1.1.1
34999  * Copyright(c) 2006-2007, Ext JS, LLC.
35000  *
35001  * Originally Released Under LGPL - original licence link has changed is not relivant.
35002  *
35003  * Fork - LGPL
35004  * <script type="text/javascript">
35005  */
35006
35007 /**
35008  * @class Roo.grid.AbstractSelectionModel
35009  * @extends Roo.util.Observable
35010  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35011  * implemented by descendant classes.  This class should not be directly instantiated.
35012  * @constructor
35013  */
35014 Roo.grid.AbstractSelectionModel = function(){
35015     this.locked = false;
35016     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35017 };
35018
35019 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35020     /** @ignore Called by the grid automatically. Do not call directly. */
35021     init : function(grid){
35022         this.grid = grid;
35023         this.initEvents();
35024     },
35025
35026     /**
35027      * Locks the selections.
35028      */
35029     lock : function(){
35030         this.locked = true;
35031     },
35032
35033     /**
35034      * Unlocks the selections.
35035      */
35036     unlock : function(){
35037         this.locked = false;
35038     },
35039
35040     /**
35041      * Returns true if the selections are locked.
35042      * @return {Boolean}
35043      */
35044     isLocked : function(){
35045         return this.locked;
35046     }
35047 });/*
35048  * Based on:
35049  * Ext JS Library 1.1.1
35050  * Copyright(c) 2006-2007, Ext JS, LLC.
35051  *
35052  * Originally Released Under LGPL - original licence link has changed is not relivant.
35053  *
35054  * Fork - LGPL
35055  * <script type="text/javascript">
35056  */
35057 /**
35058  * @extends Roo.grid.AbstractSelectionModel
35059  * @class Roo.grid.RowSelectionModel
35060  * The default SelectionModel used by {@link Roo.grid.Grid}.
35061  * It supports multiple selections and keyboard selection/navigation. 
35062  * @constructor
35063  * @param {Object} config
35064  */
35065 Roo.grid.RowSelectionModel = function(config){
35066     Roo.apply(this, config);
35067     this.selections = new Roo.util.MixedCollection(false, function(o){
35068         return o.id;
35069     });
35070
35071     this.last = false;
35072     this.lastActive = false;
35073
35074     this.addEvents({
35075         /**
35076              * @event selectionchange
35077              * Fires when the selection changes
35078              * @param {SelectionModel} this
35079              */
35080             "selectionchange" : true,
35081         /**
35082              * @event afterselectionchange
35083              * Fires after the selection changes (eg. by key press or clicking)
35084              * @param {SelectionModel} this
35085              */
35086             "afterselectionchange" : true,
35087         /**
35088              * @event beforerowselect
35089              * Fires when a row is selected being selected, return false to cancel.
35090              * @param {SelectionModel} this
35091              * @param {Number} rowIndex The selected index
35092              * @param {Boolean} keepExisting False if other selections will be cleared
35093              */
35094             "beforerowselect" : true,
35095         /**
35096              * @event rowselect
35097              * Fires when a row is selected.
35098              * @param {SelectionModel} this
35099              * @param {Number} rowIndex The selected index
35100              * @param {Roo.data.Record} r The record
35101              */
35102             "rowselect" : true,
35103         /**
35104              * @event rowdeselect
35105              * Fires when a row is deselected.
35106              * @param {SelectionModel} this
35107              * @param {Number} rowIndex The selected index
35108              */
35109         "rowdeselect" : true
35110     });
35111     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35112     this.locked = false;
35113 };
35114
35115 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35116     /**
35117      * @cfg {Boolean} singleSelect
35118      * True to allow selection of only one row at a time (defaults to false)
35119      */
35120     singleSelect : false,
35121
35122     // private
35123     initEvents : function(){
35124
35125         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35126             this.grid.on("mousedown", this.handleMouseDown, this);
35127         }else{ // allow click to work like normal
35128             this.grid.on("rowclick", this.handleDragableRowClick, this);
35129         }
35130
35131         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35132             "up" : function(e){
35133                 if(!e.shiftKey){
35134                     this.selectPrevious(e.shiftKey);
35135                 }else if(this.last !== false && this.lastActive !== false){
35136                     var last = this.last;
35137                     this.selectRange(this.last,  this.lastActive-1);
35138                     this.grid.getView().focusRow(this.lastActive);
35139                     if(last !== false){
35140                         this.last = last;
35141                     }
35142                 }else{
35143                     this.selectFirstRow();
35144                 }
35145                 this.fireEvent("afterselectionchange", this);
35146             },
35147             "down" : function(e){
35148                 if(!e.shiftKey){
35149                     this.selectNext(e.shiftKey);
35150                 }else if(this.last !== false && this.lastActive !== false){
35151                     var last = this.last;
35152                     this.selectRange(this.last,  this.lastActive+1);
35153                     this.grid.getView().focusRow(this.lastActive);
35154                     if(last !== false){
35155                         this.last = last;
35156                     }
35157                 }else{
35158                     this.selectFirstRow();
35159                 }
35160                 this.fireEvent("afterselectionchange", this);
35161             },
35162             scope: this
35163         });
35164
35165         var view = this.grid.view;
35166         view.on("refresh", this.onRefresh, this);
35167         view.on("rowupdated", this.onRowUpdated, this);
35168         view.on("rowremoved", this.onRemove, this);
35169     },
35170
35171     // private
35172     onRefresh : function(){
35173         var ds = this.grid.dataSource, i, v = this.grid.view;
35174         var s = this.selections;
35175         s.each(function(r){
35176             if((i = ds.indexOfId(r.id)) != -1){
35177                 v.onRowSelect(i);
35178             }else{
35179                 s.remove(r);
35180             }
35181         });
35182     },
35183
35184     // private
35185     onRemove : function(v, index, r){
35186         this.selections.remove(r);
35187     },
35188
35189     // private
35190     onRowUpdated : function(v, index, r){
35191         if(this.isSelected(r)){
35192             v.onRowSelect(index);
35193         }
35194     },
35195
35196     /**
35197      * Select records.
35198      * @param {Array} records The records to select
35199      * @param {Boolean} keepExisting (optional) True to keep existing selections
35200      */
35201     selectRecords : function(records, keepExisting){
35202         if(!keepExisting){
35203             this.clearSelections();
35204         }
35205         var ds = this.grid.dataSource;
35206         for(var i = 0, len = records.length; i < len; i++){
35207             this.selectRow(ds.indexOf(records[i]), true);
35208         }
35209     },
35210
35211     /**
35212      * Gets the number of selected rows.
35213      * @return {Number}
35214      */
35215     getCount : function(){
35216         return this.selections.length;
35217     },
35218
35219     /**
35220      * Selects the first row in the grid.
35221      */
35222     selectFirstRow : function(){
35223         this.selectRow(0);
35224     },
35225
35226     /**
35227      * Select the last row.
35228      * @param {Boolean} keepExisting (optional) True to keep existing selections
35229      */
35230     selectLastRow : function(keepExisting){
35231         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35232     },
35233
35234     /**
35235      * Selects the row immediately following the last selected row.
35236      * @param {Boolean} keepExisting (optional) True to keep existing selections
35237      */
35238     selectNext : function(keepExisting){
35239         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35240             this.selectRow(this.last+1, keepExisting);
35241             this.grid.getView().focusRow(this.last);
35242         }
35243     },
35244
35245     /**
35246      * Selects the row that precedes the last selected row.
35247      * @param {Boolean} keepExisting (optional) True to keep existing selections
35248      */
35249     selectPrevious : function(keepExisting){
35250         if(this.last){
35251             this.selectRow(this.last-1, keepExisting);
35252             this.grid.getView().focusRow(this.last);
35253         }
35254     },
35255
35256     /**
35257      * Returns the selected records
35258      * @return {Array} Array of selected records
35259      */
35260     getSelections : function(){
35261         return [].concat(this.selections.items);
35262     },
35263
35264     /**
35265      * Returns the first selected record.
35266      * @return {Record}
35267      */
35268     getSelected : function(){
35269         return this.selections.itemAt(0);
35270     },
35271
35272
35273     /**
35274      * Clears all selections.
35275      */
35276     clearSelections : function(fast){
35277         if(this.locked) return;
35278         if(fast !== true){
35279             var ds = this.grid.dataSource;
35280             var s = this.selections;
35281             s.each(function(r){
35282                 this.deselectRow(ds.indexOfId(r.id));
35283             }, this);
35284             s.clear();
35285         }else{
35286             this.selections.clear();
35287         }
35288         this.last = false;
35289     },
35290
35291
35292     /**
35293      * Selects all rows.
35294      */
35295     selectAll : function(){
35296         if(this.locked) return;
35297         this.selections.clear();
35298         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35299             this.selectRow(i, true);
35300         }
35301     },
35302
35303     /**
35304      * Returns True if there is a selection.
35305      * @return {Boolean}
35306      */
35307     hasSelection : function(){
35308         return this.selections.length > 0;
35309     },
35310
35311     /**
35312      * Returns True if the specified row is selected.
35313      * @param {Number/Record} record The record or index of the record to check
35314      * @return {Boolean}
35315      */
35316     isSelected : function(index){
35317         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35318         return (r && this.selections.key(r.id) ? true : false);
35319     },
35320
35321     /**
35322      * Returns True if the specified record id is selected.
35323      * @param {String} id The id of record to check
35324      * @return {Boolean}
35325      */
35326     isIdSelected : function(id){
35327         return (this.selections.key(id) ? true : false);
35328     },
35329
35330     // private
35331     handleMouseDown : function(e, t){
35332         var view = this.grid.getView(), rowIndex;
35333         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35334             return;
35335         };
35336         if(e.shiftKey && this.last !== false){
35337             var last = this.last;
35338             this.selectRange(last, rowIndex, e.ctrlKey);
35339             this.last = last; // reset the last
35340             view.focusRow(rowIndex);
35341         }else{
35342             var isSelected = this.isSelected(rowIndex);
35343             if(e.button !== 0 && isSelected){
35344                 view.focusRow(rowIndex);
35345             }else if(e.ctrlKey && isSelected){
35346                 this.deselectRow(rowIndex);
35347             }else if(!isSelected){
35348                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35349                 view.focusRow(rowIndex);
35350             }
35351         }
35352         this.fireEvent("afterselectionchange", this);
35353     },
35354     // private
35355     handleDragableRowClick :  function(grid, rowIndex, e) 
35356     {
35357         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35358             this.selectRow(rowIndex, false);
35359             grid.view.focusRow(rowIndex);
35360              this.fireEvent("afterselectionchange", this);
35361         }
35362     },
35363     
35364     /**
35365      * Selects multiple rows.
35366      * @param {Array} rows Array of the indexes of the row to select
35367      * @param {Boolean} keepExisting (optional) True to keep existing selections
35368      */
35369     selectRows : function(rows, keepExisting){
35370         if(!keepExisting){
35371             this.clearSelections();
35372         }
35373         for(var i = 0, len = rows.length; i < len; i++){
35374             this.selectRow(rows[i], true);
35375         }
35376     },
35377
35378     /**
35379      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35380      * @param {Number} startRow The index of the first row in the range
35381      * @param {Number} endRow The index of the last row in the range
35382      * @param {Boolean} keepExisting (optional) True to retain existing selections
35383      */
35384     selectRange : function(startRow, endRow, keepExisting){
35385         if(this.locked) return;
35386         if(!keepExisting){
35387             this.clearSelections();
35388         }
35389         if(startRow <= endRow){
35390             for(var i = startRow; i <= endRow; i++){
35391                 this.selectRow(i, true);
35392             }
35393         }else{
35394             for(var i = startRow; i >= endRow; i--){
35395                 this.selectRow(i, true);
35396             }
35397         }
35398     },
35399
35400     /**
35401      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35402      * @param {Number} startRow The index of the first row in the range
35403      * @param {Number} endRow The index of the last row in the range
35404      */
35405     deselectRange : function(startRow, endRow, preventViewNotify){
35406         if(this.locked) return;
35407         for(var i = startRow; i <= endRow; i++){
35408             this.deselectRow(i, preventViewNotify);
35409         }
35410     },
35411
35412     /**
35413      * Selects a row.
35414      * @param {Number} row The index of the row to select
35415      * @param {Boolean} keepExisting (optional) True to keep existing selections
35416      */
35417     selectRow : function(index, keepExisting, preventViewNotify){
35418         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35419         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35420             if(!keepExisting || this.singleSelect){
35421                 this.clearSelections();
35422             }
35423             var r = this.grid.dataSource.getAt(index);
35424             this.selections.add(r);
35425             this.last = this.lastActive = index;
35426             if(!preventViewNotify){
35427                 this.grid.getView().onRowSelect(index);
35428             }
35429             this.fireEvent("rowselect", this, index, r);
35430             this.fireEvent("selectionchange", this);
35431         }
35432     },
35433
35434     /**
35435      * Deselects a row.
35436      * @param {Number} row The index of the row to deselect
35437      */
35438     deselectRow : function(index, preventViewNotify){
35439         if(this.locked) return;
35440         if(this.last == index){
35441             this.last = false;
35442         }
35443         if(this.lastActive == index){
35444             this.lastActive = false;
35445         }
35446         var r = this.grid.dataSource.getAt(index);
35447         this.selections.remove(r);
35448         if(!preventViewNotify){
35449             this.grid.getView().onRowDeselect(index);
35450         }
35451         this.fireEvent("rowdeselect", this, index);
35452         this.fireEvent("selectionchange", this);
35453     },
35454
35455     // private
35456     restoreLast : function(){
35457         if(this._last){
35458             this.last = this._last;
35459         }
35460     },
35461
35462     // private
35463     acceptsNav : function(row, col, cm){
35464         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35465     },
35466
35467     // private
35468     onEditorKey : function(field, e){
35469         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35470         if(k == e.TAB){
35471             e.stopEvent();
35472             ed.completeEdit();
35473             if(e.shiftKey){
35474                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35475             }else{
35476                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35477             }
35478         }else if(k == e.ENTER && !e.ctrlKey){
35479             e.stopEvent();
35480             ed.completeEdit();
35481             if(e.shiftKey){
35482                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35483             }else{
35484                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35485             }
35486         }else if(k == e.ESC){
35487             ed.cancelEdit();
35488         }
35489         if(newCell){
35490             g.startEditing(newCell[0], newCell[1]);
35491         }
35492     }
35493 });/*
35494  * Based on:
35495  * Ext JS Library 1.1.1
35496  * Copyright(c) 2006-2007, Ext JS, LLC.
35497  *
35498  * Originally Released Under LGPL - original licence link has changed is not relivant.
35499  *
35500  * Fork - LGPL
35501  * <script type="text/javascript">
35502  */
35503 /**
35504  * @class Roo.grid.CellSelectionModel
35505  * @extends Roo.grid.AbstractSelectionModel
35506  * This class provides the basic implementation for cell selection in a grid.
35507  * @constructor
35508  * @param {Object} config The object containing the configuration of this model.
35509  */
35510 Roo.grid.CellSelectionModel = function(config){
35511     Roo.apply(this, config);
35512
35513     this.selection = null;
35514
35515     this.addEvents({
35516         /**
35517              * @event beforerowselect
35518              * Fires before a cell is selected.
35519              * @param {SelectionModel} this
35520              * @param {Number} rowIndex The selected row index
35521              * @param {Number} colIndex The selected cell index
35522              */
35523             "beforecellselect" : true,
35524         /**
35525              * @event cellselect
35526              * Fires when a cell is selected.
35527              * @param {SelectionModel} this
35528              * @param {Number} rowIndex The selected row index
35529              * @param {Number} colIndex The selected cell index
35530              */
35531             "cellselect" : true,
35532         /**
35533              * @event selectionchange
35534              * Fires when the active selection changes.
35535              * @param {SelectionModel} this
35536              * @param {Object} selection null for no selection or an object (o) with two properties
35537                 <ul>
35538                 <li>o.record: the record object for the row the selection is in</li>
35539                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35540                 </ul>
35541              */
35542             "selectionchange" : true
35543     });
35544     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35545 };
35546
35547 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35548
35549     /** @ignore */
35550     initEvents : function(){
35551         this.grid.on("mousedown", this.handleMouseDown, this);
35552         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35553         var view = this.grid.view;
35554         view.on("refresh", this.onViewChange, this);
35555         view.on("rowupdated", this.onRowUpdated, this);
35556         view.on("beforerowremoved", this.clearSelections, this);
35557         view.on("beforerowsinserted", this.clearSelections, this);
35558         if(this.grid.isEditor){
35559             this.grid.on("beforeedit", this.beforeEdit,  this);
35560         }
35561     },
35562
35563         //private
35564     beforeEdit : function(e){
35565         this.select(e.row, e.column, false, true, e.record);
35566     },
35567
35568         //private
35569     onRowUpdated : function(v, index, r){
35570         if(this.selection && this.selection.record == r){
35571             v.onCellSelect(index, this.selection.cell[1]);
35572         }
35573     },
35574
35575         //private
35576     onViewChange : function(){
35577         this.clearSelections(true);
35578     },
35579
35580         /**
35581          * Returns the currently selected cell,.
35582          * @return {Array} The selected cell (row, column) or null if none selected.
35583          */
35584     getSelectedCell : function(){
35585         return this.selection ? this.selection.cell : null;
35586     },
35587
35588     /**
35589      * Clears all selections.
35590      * @param {Boolean} true to prevent the gridview from being notified about the change.
35591      */
35592     clearSelections : function(preventNotify){
35593         var s = this.selection;
35594         if(s){
35595             if(preventNotify !== true){
35596                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35597             }
35598             this.selection = null;
35599             this.fireEvent("selectionchange", this, null);
35600         }
35601     },
35602
35603     /**
35604      * Returns true if there is a selection.
35605      * @return {Boolean}
35606      */
35607     hasSelection : function(){
35608         return this.selection ? true : false;
35609     },
35610
35611     /** @ignore */
35612     handleMouseDown : function(e, t){
35613         var v = this.grid.getView();
35614         if(this.isLocked()){
35615             return;
35616         };
35617         var row = v.findRowIndex(t);
35618         var cell = v.findCellIndex(t);
35619         if(row !== false && cell !== false){
35620             this.select(row, cell);
35621         }
35622     },
35623
35624     /**
35625      * Selects a cell.
35626      * @param {Number} rowIndex
35627      * @param {Number} collIndex
35628      */
35629     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35630         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35631             this.clearSelections();
35632             r = r || this.grid.dataSource.getAt(rowIndex);
35633             this.selection = {
35634                 record : r,
35635                 cell : [rowIndex, colIndex]
35636             };
35637             if(!preventViewNotify){
35638                 var v = this.grid.getView();
35639                 v.onCellSelect(rowIndex, colIndex);
35640                 if(preventFocus !== true){
35641                     v.focusCell(rowIndex, colIndex);
35642                 }
35643             }
35644             this.fireEvent("cellselect", this, rowIndex, colIndex);
35645             this.fireEvent("selectionchange", this, this.selection);
35646         }
35647     },
35648
35649         //private
35650     isSelectable : function(rowIndex, colIndex, cm){
35651         return !cm.isHidden(colIndex);
35652     },
35653
35654     /** @ignore */
35655     handleKeyDown : function(e){
35656         Roo.log('Cell Sel Model handleKeyDown');
35657         if(!e.isNavKeyPress()){
35658             return;
35659         }
35660         var g = this.grid, s = this.selection;
35661         if(!s){
35662             e.stopEvent();
35663             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35664             if(cell){
35665                 this.select(cell[0], cell[1]);
35666             }
35667             return;
35668         }
35669         var sm = this;
35670         var walk = function(row, col, step){
35671             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35672         };
35673         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35674         var newCell;
35675
35676         switch(k){
35677             case e.TAB:
35678                 // handled by onEditorKey
35679                 if (g.isEditor && g.editing) {
35680                     return;
35681                 }
35682                 if(e.shiftKey){
35683                      newCell = walk(r, c-1, -1);
35684                 }else{
35685                      newCell = walk(r, c+1, 1);
35686                 }
35687              break;
35688              case e.DOWN:
35689                  newCell = walk(r+1, c, 1);
35690              break;
35691              case e.UP:
35692                  newCell = walk(r-1, c, -1);
35693              break;
35694              case e.RIGHT:
35695                  newCell = walk(r, c+1, 1);
35696              break;
35697              case e.LEFT:
35698                  newCell = walk(r, c-1, -1);
35699              break;
35700              case e.ENTER:
35701                  if(g.isEditor && !g.editing){
35702                     g.startEditing(r, c);
35703                     e.stopEvent();
35704                     return;
35705                 }
35706              break;
35707         };
35708         if(newCell){
35709             this.select(newCell[0], newCell[1]);
35710             e.stopEvent();
35711         }
35712     },
35713
35714     acceptsNav : function(row, col, cm){
35715         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35716     },
35717
35718     onEditorKey : function(field, e){
35719         
35720         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35721         ///Roo.log('onEditorKey' + k);
35722         
35723         if(k == e.TAB){
35724             if(e.shiftKey){
35725                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35726             }else{
35727                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35728             }
35729             e.stopEvent();
35730         }else if(k == e.ENTER && !e.ctrlKey){
35731             ed.completeEdit();
35732             e.stopEvent();
35733             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35734         }else if(k == e.ESC){
35735             ed.cancelEdit();
35736         }
35737         
35738         
35739         if(newCell){
35740             //Roo.log('next cell after edit');
35741             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35742         }
35743     }
35744 });/*
35745  * Based on:
35746  * Ext JS Library 1.1.1
35747  * Copyright(c) 2006-2007, Ext JS, LLC.
35748  *
35749  * Originally Released Under LGPL - original licence link has changed is not relivant.
35750  *
35751  * Fork - LGPL
35752  * <script type="text/javascript">
35753  */
35754  
35755 /**
35756  * @class Roo.grid.EditorGrid
35757  * @extends Roo.grid.Grid
35758  * Class for creating and editable grid.
35759  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35760  * The container MUST have some type of size defined for the grid to fill. The container will be 
35761  * automatically set to position relative if it isn't already.
35762  * @param {Object} dataSource The data model to bind to
35763  * @param {Object} colModel The column model with info about this grid's columns
35764  */
35765 Roo.grid.EditorGrid = function(container, config){
35766     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35767     this.getGridEl().addClass("xedit-grid");
35768
35769     if(!this.selModel){
35770         this.selModel = new Roo.grid.CellSelectionModel();
35771     }
35772
35773     this.activeEditor = null;
35774
35775         this.addEvents({
35776             /**
35777              * @event beforeedit
35778              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35779              * <ul style="padding:5px;padding-left:16px;">
35780              * <li>grid - This grid</li>
35781              * <li>record - The record being edited</li>
35782              * <li>field - The field name being edited</li>
35783              * <li>value - The value for the field being edited.</li>
35784              * <li>row - The grid row index</li>
35785              * <li>column - The grid column index</li>
35786              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35787              * </ul>
35788              * @param {Object} e An edit event (see above for description)
35789              */
35790             "beforeedit" : true,
35791             /**
35792              * @event afteredit
35793              * Fires after a cell is edited. <br />
35794              * <ul style="padding:5px;padding-left:16px;">
35795              * <li>grid - This grid</li>
35796              * <li>record - The record being edited</li>
35797              * <li>field - The field name being edited</li>
35798              * <li>value - The value being set</li>
35799              * <li>originalValue - The original value for the field, before the edit.</li>
35800              * <li>row - The grid row index</li>
35801              * <li>column - The grid column index</li>
35802              * </ul>
35803              * @param {Object} e An edit event (see above for description)
35804              */
35805             "afteredit" : true,
35806             /**
35807              * @event validateedit
35808              * Fires after a cell is edited, but before the value is set in the record. 
35809          * You can use this to modify the value being set in the field, Return false
35810              * to cancel the change. The edit event object has the following properties <br />
35811              * <ul style="padding:5px;padding-left:16px;">
35812          * <li>editor - This editor</li>
35813              * <li>grid - This grid</li>
35814              * <li>record - The record being edited</li>
35815              * <li>field - The field name being edited</li>
35816              * <li>value - The value being set</li>
35817              * <li>originalValue - The original value for the field, before the edit.</li>
35818              * <li>row - The grid row index</li>
35819              * <li>column - The grid column index</li>
35820              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35821              * </ul>
35822              * @param {Object} e An edit event (see above for description)
35823              */
35824             "validateedit" : true
35825         });
35826     this.on("bodyscroll", this.stopEditing,  this);
35827     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35828 };
35829
35830 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35831     /**
35832      * @cfg {Number} clicksToEdit
35833      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35834      */
35835     clicksToEdit: 2,
35836
35837     // private
35838     isEditor : true,
35839     // private
35840     trackMouseOver: false, // causes very odd FF errors
35841
35842     onCellDblClick : function(g, row, col){
35843         this.startEditing(row, col);
35844     },
35845
35846     onEditComplete : function(ed, value, startValue){
35847         this.editing = false;
35848         this.activeEditor = null;
35849         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35850         var r = ed.record;
35851         var field = this.colModel.getDataIndex(ed.col);
35852         var e = {
35853             grid: this,
35854             record: r,
35855             field: field,
35856             originalValue: startValue,
35857             value: value,
35858             row: ed.row,
35859             column: ed.col,
35860             cancel:false,
35861             editor: ed
35862         };
35863         if(String(value) !== String(startValue)){
35864             
35865             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35866                 r.set(field, e.value);
35867                 // if we are dealing with a combo box..
35868                 // then we also set the 'name' colum to be the displayField
35869                 if (ed.field.displayField && ed.field.name) {
35870                     r.set(ed.field.name, ed.field.el.dom.value);
35871                 }
35872                 
35873                 delete e.cancel; //?? why!!!
35874                 this.fireEvent("afteredit", e);
35875             }
35876         } else {
35877             this.fireEvent("afteredit", e); // always fire it!
35878         }
35879         this.view.focusCell(ed.row, ed.col);
35880     },
35881
35882     /**
35883      * Starts editing the specified for the specified row/column
35884      * @param {Number} rowIndex
35885      * @param {Number} colIndex
35886      */
35887     startEditing : function(row, col){
35888         this.stopEditing();
35889         if(this.colModel.isCellEditable(col, row)){
35890             this.view.ensureVisible(row, col, true);
35891             var r = this.dataSource.getAt(row);
35892             var field = this.colModel.getDataIndex(col);
35893             var e = {
35894                 grid: this,
35895                 record: r,
35896                 field: field,
35897                 value: r.data[field],
35898                 row: row,
35899                 column: col,
35900                 cancel:false
35901             };
35902             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35903                 this.editing = true;
35904                 var ed = this.colModel.getCellEditor(col, row);
35905                 
35906                 if (!ed) {
35907                     return;
35908                 }
35909                 if(!ed.rendered){
35910                     ed.render(ed.parentEl || document.body);
35911                 }
35912                 ed.field.reset();
35913                 (function(){ // complex but required for focus issues in safari, ie and opera
35914                     ed.row = row;
35915                     ed.col = col;
35916                     ed.record = r;
35917                     ed.on("complete", this.onEditComplete, this, {single: true});
35918                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35919                     this.activeEditor = ed;
35920                     var v = r.data[field];
35921                     ed.startEdit(this.view.getCell(row, col), v);
35922                     // combo's with 'displayField and name set
35923                     if (ed.field.displayField && ed.field.name) {
35924                         ed.field.el.dom.value = r.data[ed.field.name];
35925                     }
35926                     
35927                     
35928                 }).defer(50, this);
35929             }
35930         }
35931     },
35932         
35933     /**
35934      * Stops any active editing
35935      */
35936     stopEditing : function(){
35937         if(this.activeEditor){
35938             this.activeEditor.completeEdit();
35939         }
35940         this.activeEditor = null;
35941     }
35942 });/*
35943  * Based on:
35944  * Ext JS Library 1.1.1
35945  * Copyright(c) 2006-2007, Ext JS, LLC.
35946  *
35947  * Originally Released Under LGPL - original licence link has changed is not relivant.
35948  *
35949  * Fork - LGPL
35950  * <script type="text/javascript">
35951  */
35952
35953 // private - not really -- you end up using it !
35954 // This is a support class used internally by the Grid components
35955
35956 /**
35957  * @class Roo.grid.GridEditor
35958  * @extends Roo.Editor
35959  * Class for creating and editable grid elements.
35960  * @param {Object} config any settings (must include field)
35961  */
35962 Roo.grid.GridEditor = function(field, config){
35963     if (!config && field.field) {
35964         config = field;
35965         field = Roo.factory(config.field, Roo.form);
35966     }
35967     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35968     field.monitorTab = false;
35969 };
35970
35971 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35972     
35973     /**
35974      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35975      */
35976     
35977     alignment: "tl-tl",
35978     autoSize: "width",
35979     hideEl : false,
35980     cls: "x-small-editor x-grid-editor",
35981     shim:false,
35982     shadow:"frame"
35983 });/*
35984  * Based on:
35985  * Ext JS Library 1.1.1
35986  * Copyright(c) 2006-2007, Ext JS, LLC.
35987  *
35988  * Originally Released Under LGPL - original licence link has changed is not relivant.
35989  *
35990  * Fork - LGPL
35991  * <script type="text/javascript">
35992  */
35993   
35994
35995   
35996 Roo.grid.PropertyRecord = Roo.data.Record.create([
35997     {name:'name',type:'string'},  'value'
35998 ]);
35999
36000
36001 Roo.grid.PropertyStore = function(grid, source){
36002     this.grid = grid;
36003     this.store = new Roo.data.Store({
36004         recordType : Roo.grid.PropertyRecord
36005     });
36006     this.store.on('update', this.onUpdate,  this);
36007     if(source){
36008         this.setSource(source);
36009     }
36010     Roo.grid.PropertyStore.superclass.constructor.call(this);
36011 };
36012
36013
36014
36015 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36016     setSource : function(o){
36017         this.source = o;
36018         this.store.removeAll();
36019         var data = [];
36020         for(var k in o){
36021             if(this.isEditableValue(o[k])){
36022                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36023             }
36024         }
36025         this.store.loadRecords({records: data}, {}, true);
36026     },
36027
36028     onUpdate : function(ds, record, type){
36029         if(type == Roo.data.Record.EDIT){
36030             var v = record.data['value'];
36031             var oldValue = record.modified['value'];
36032             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36033                 this.source[record.id] = v;
36034                 record.commit();
36035                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36036             }else{
36037                 record.reject();
36038             }
36039         }
36040     },
36041
36042     getProperty : function(row){
36043        return this.store.getAt(row);
36044     },
36045
36046     isEditableValue: function(val){
36047         if(val && val instanceof Date){
36048             return true;
36049         }else if(typeof val == 'object' || typeof val == 'function'){
36050             return false;
36051         }
36052         return true;
36053     },
36054
36055     setValue : function(prop, value){
36056         this.source[prop] = value;
36057         this.store.getById(prop).set('value', value);
36058     },
36059
36060     getSource : function(){
36061         return this.source;
36062     }
36063 });
36064
36065 Roo.grid.PropertyColumnModel = function(grid, store){
36066     this.grid = grid;
36067     var g = Roo.grid;
36068     g.PropertyColumnModel.superclass.constructor.call(this, [
36069         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36070         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36071     ]);
36072     this.store = store;
36073     this.bselect = Roo.DomHelper.append(document.body, {
36074         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36075             {tag: 'option', value: 'true', html: 'true'},
36076             {tag: 'option', value: 'false', html: 'false'}
36077         ]
36078     });
36079     Roo.id(this.bselect);
36080     var f = Roo.form;
36081     this.editors = {
36082         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36083         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36084         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36085         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36086         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36087     };
36088     this.renderCellDelegate = this.renderCell.createDelegate(this);
36089     this.renderPropDelegate = this.renderProp.createDelegate(this);
36090 };
36091
36092 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36093     
36094     
36095     nameText : 'Name',
36096     valueText : 'Value',
36097     
36098     dateFormat : 'm/j/Y',
36099     
36100     
36101     renderDate : function(dateVal){
36102         return dateVal.dateFormat(this.dateFormat);
36103     },
36104
36105     renderBool : function(bVal){
36106         return bVal ? 'true' : 'false';
36107     },
36108
36109     isCellEditable : function(colIndex, rowIndex){
36110         return colIndex == 1;
36111     },
36112
36113     getRenderer : function(col){
36114         return col == 1 ?
36115             this.renderCellDelegate : this.renderPropDelegate;
36116     },
36117
36118     renderProp : function(v){
36119         return this.getPropertyName(v);
36120     },
36121
36122     renderCell : function(val){
36123         var rv = val;
36124         if(val instanceof Date){
36125             rv = this.renderDate(val);
36126         }else if(typeof val == 'boolean'){
36127             rv = this.renderBool(val);
36128         }
36129         return Roo.util.Format.htmlEncode(rv);
36130     },
36131
36132     getPropertyName : function(name){
36133         var pn = this.grid.propertyNames;
36134         return pn && pn[name] ? pn[name] : name;
36135     },
36136
36137     getCellEditor : function(colIndex, rowIndex){
36138         var p = this.store.getProperty(rowIndex);
36139         var n = p.data['name'], val = p.data['value'];
36140         
36141         if(typeof(this.grid.customEditors[n]) == 'string'){
36142             return this.editors[this.grid.customEditors[n]];
36143         }
36144         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36145             return this.grid.customEditors[n];
36146         }
36147         if(val instanceof Date){
36148             return this.editors['date'];
36149         }else if(typeof val == 'number'){
36150             return this.editors['number'];
36151         }else if(typeof val == 'boolean'){
36152             return this.editors['boolean'];
36153         }else{
36154             return this.editors['string'];
36155         }
36156     }
36157 });
36158
36159 /**
36160  * @class Roo.grid.PropertyGrid
36161  * @extends Roo.grid.EditorGrid
36162  * This class represents the  interface of a component based property grid control.
36163  * <br><br>Usage:<pre><code>
36164  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36165       
36166  });
36167  // set any options
36168  grid.render();
36169  * </code></pre>
36170   
36171  * @constructor
36172  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36173  * The container MUST have some type of size defined for the grid to fill. The container will be
36174  * automatically set to position relative if it isn't already.
36175  * @param {Object} config A config object that sets properties on this grid.
36176  */
36177 Roo.grid.PropertyGrid = function(container, config){
36178     config = config || {};
36179     var store = new Roo.grid.PropertyStore(this);
36180     this.store = store;
36181     var cm = new Roo.grid.PropertyColumnModel(this, store);
36182     store.store.sort('name', 'ASC');
36183     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36184         ds: store.store,
36185         cm: cm,
36186         enableColLock:false,
36187         enableColumnMove:false,
36188         stripeRows:false,
36189         trackMouseOver: false,
36190         clicksToEdit:1
36191     }, config));
36192     this.getGridEl().addClass('x-props-grid');
36193     this.lastEditRow = null;
36194     this.on('columnresize', this.onColumnResize, this);
36195     this.addEvents({
36196          /**
36197              * @event beforepropertychange
36198              * Fires before a property changes (return false to stop?)
36199              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36200              * @param {String} id Record Id
36201              * @param {String} newval New Value
36202          * @param {String} oldval Old Value
36203              */
36204         "beforepropertychange": true,
36205         /**
36206              * @event propertychange
36207              * Fires after a property changes
36208              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36209              * @param {String} id Record Id
36210              * @param {String} newval New Value
36211          * @param {String} oldval Old Value
36212              */
36213         "propertychange": true
36214     });
36215     this.customEditors = this.customEditors || {};
36216 };
36217 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36218     
36219      /**
36220      * @cfg {Object} customEditors map of colnames=> custom editors.
36221      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36222      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36223      * false disables editing of the field.
36224          */
36225     
36226       /**
36227      * @cfg {Object} propertyNames map of property Names to their displayed value
36228          */
36229     
36230     render : function(){
36231         Roo.grid.PropertyGrid.superclass.render.call(this);
36232         this.autoSize.defer(100, this);
36233     },
36234
36235     autoSize : function(){
36236         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36237         if(this.view){
36238             this.view.fitColumns();
36239         }
36240     },
36241
36242     onColumnResize : function(){
36243         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36244         this.autoSize();
36245     },
36246     /**
36247      * Sets the data for the Grid
36248      * accepts a Key => Value object of all the elements avaiable.
36249      * @param {Object} data  to appear in grid.
36250      */
36251     setSource : function(source){
36252         this.store.setSource(source);
36253         //this.autoSize();
36254     },
36255     /**
36256      * Gets all the data from the grid.
36257      * @return {Object} data  data stored in grid
36258      */
36259     getSource : function(){
36260         return this.store.getSource();
36261     }
36262 });/*
36263  * Based on:
36264  * Ext JS Library 1.1.1
36265  * Copyright(c) 2006-2007, Ext JS, LLC.
36266  *
36267  * Originally Released Under LGPL - original licence link has changed is not relivant.
36268  *
36269  * Fork - LGPL
36270  * <script type="text/javascript">
36271  */
36272  
36273 /**
36274  * @class Roo.LoadMask
36275  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36276  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36277  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36278  * element's UpdateManager load indicator and will be destroyed after the initial load.
36279  * @constructor
36280  * Create a new LoadMask
36281  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36282  * @param {Object} config The config object
36283  */
36284 Roo.LoadMask = function(el, config){
36285     this.el = Roo.get(el);
36286     Roo.apply(this, config);
36287     if(this.store){
36288         this.store.on('beforeload', this.onBeforeLoad, this);
36289         this.store.on('load', this.onLoad, this);
36290         this.store.on('loadexception', this.onLoad, this);
36291         this.removeMask = false;
36292     }else{
36293         var um = this.el.getUpdateManager();
36294         um.showLoadIndicator = false; // disable the default indicator
36295         um.on('beforeupdate', this.onBeforeLoad, this);
36296         um.on('update', this.onLoad, this);
36297         um.on('failure', this.onLoad, this);
36298         this.removeMask = true;
36299     }
36300 };
36301
36302 Roo.LoadMask.prototype = {
36303     /**
36304      * @cfg {Boolean} removeMask
36305      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36306      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36307      */
36308     /**
36309      * @cfg {String} msg
36310      * The text to display in a centered loading message box (defaults to 'Loading...')
36311      */
36312     msg : 'Loading...',
36313     /**
36314      * @cfg {String} msgCls
36315      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36316      */
36317     msgCls : 'x-mask-loading',
36318
36319     /**
36320      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36321      * @type Boolean
36322      */
36323     disabled: false,
36324
36325     /**
36326      * Disables the mask to prevent it from being displayed
36327      */
36328     disable : function(){
36329        this.disabled = true;
36330     },
36331
36332     /**
36333      * Enables the mask so that it can be displayed
36334      */
36335     enable : function(){
36336         this.disabled = false;
36337     },
36338
36339     // private
36340     onLoad : function(){
36341         this.el.unmask(this.removeMask);
36342     },
36343
36344     // private
36345     onBeforeLoad : function(){
36346         if(!this.disabled){
36347             this.el.mask(this.msg, this.msgCls);
36348         }
36349     },
36350
36351     // private
36352     destroy : function(){
36353         if(this.store){
36354             this.store.un('beforeload', this.onBeforeLoad, this);
36355             this.store.un('load', this.onLoad, this);
36356             this.store.un('loadexception', this.onLoad, this);
36357         }else{
36358             var um = this.el.getUpdateManager();
36359             um.un('beforeupdate', this.onBeforeLoad, this);
36360             um.un('update', this.onLoad, this);
36361             um.un('failure', this.onLoad, this);
36362         }
36363     }
36364 };/*
36365  * Based on:
36366  * Ext JS Library 1.1.1
36367  * Copyright(c) 2006-2007, Ext JS, LLC.
36368  *
36369  * Originally Released Under LGPL - original licence link has changed is not relivant.
36370  *
36371  * Fork - LGPL
36372  * <script type="text/javascript">
36373  */
36374 Roo.XTemplate = function(){
36375     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36376     var s = this.html;
36377
36378     s = ['<tpl>', s, '</tpl>'].join('');
36379
36380     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36381
36382     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36383     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36384     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36385     var m, id = 0;
36386     var tpls = [];
36387
36388     while(m = s.match(re)){
36389        var m2 = m[0].match(nameRe);
36390        var m3 = m[0].match(ifRe);
36391        var m4 = m[0].match(execRe);
36392        var exp = null, fn = null, exec = null;
36393        var name = m2 && m2[1] ? m2[1] : '';
36394        if(m3){
36395            exp = m3 && m3[1] ? m3[1] : null;
36396            if(exp){
36397                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36398            }
36399        }
36400        if(m4){
36401            exp = m4 && m4[1] ? m4[1] : null;
36402            if(exp){
36403                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36404            }
36405        }
36406        if(name){
36407            switch(name){
36408                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36409                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36410                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36411            }
36412        }
36413        tpls.push({
36414             id: id,
36415             target: name,
36416             exec: exec,
36417             test: fn,
36418             body: m[1]||''
36419         });
36420        s = s.replace(m[0], '{xtpl'+ id + '}');
36421        ++id;
36422     }
36423     for(var i = tpls.length-1; i >= 0; --i){
36424         this.compileTpl(tpls[i]);
36425     }
36426     this.master = tpls[tpls.length-1];
36427     this.tpls = tpls;
36428 };
36429 Roo.extend(Roo.XTemplate, Roo.Template, {
36430
36431     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36432
36433     applySubTemplate : function(id, values, parent){
36434         var t = this.tpls[id];
36435         if(t.test && !t.test.call(this, values, parent)){
36436             return '';
36437         }
36438         if(t.exec && t.exec.call(this, values, parent)){
36439             return '';
36440         }
36441         var vs = t.target ? t.target.call(this, values, parent) : values;
36442         parent = t.target ? values : parent;
36443         if(t.target && vs instanceof Array){
36444             var buf = [];
36445             for(var i = 0, len = vs.length; i < len; i++){
36446                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36447             }
36448             return buf.join('');
36449         }
36450         return t.compiled.call(this, vs, parent);
36451     },
36452
36453     compileTpl : function(tpl){
36454         var fm = Roo.util.Format;
36455         var useF = this.disableFormats !== true;
36456         var sep = Roo.isGecko ? "+" : ",";
36457         var fn = function(m, name, format, args){
36458             if(name.substr(0, 4) == 'xtpl'){
36459                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36460             }
36461             var v;
36462             if(name.indexOf('.') != -1){
36463                 v = name;
36464             }else{
36465                 v = "values['" + name + "']";
36466             }
36467             if(format && useF){
36468                 args = args ? ',' + args : "";
36469                 if(format.substr(0, 5) != "this."){
36470                     format = "fm." + format + '(';
36471                 }else{
36472                     format = 'this.call("'+ format.substr(5) + '", ';
36473                     args = ", values";
36474                 }
36475             }else{
36476                 args= ''; format = "("+v+" === undefined ? '' : ";
36477             }
36478             return "'"+ sep + format + v + args + ")"+sep+"'";
36479         };
36480         var body;
36481         // branched to use + in gecko and [].join() in others
36482         if(Roo.isGecko){
36483             body = "tpl.compiled = function(values, parent){ return '" +
36484                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36485                     "';};";
36486         }else{
36487             body = ["tpl.compiled = function(values, parent){ return ['"];
36488             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36489             body.push("'].join('');};");
36490             body = body.join('');
36491         }
36492         /** eval:var:zzzzzzz */
36493         eval(body);
36494         return this;
36495     },
36496
36497     applyTemplate : function(values){
36498         return this.master.compiled.call(this, values, {});
36499         var s = this.subs;
36500     },
36501
36502     apply : function(){
36503         return this.applyTemplate.apply(this, arguments);
36504     },
36505
36506     compile : function(){return this;}
36507 });
36508
36509 Roo.XTemplate.from = function(el){
36510     el = Roo.getDom(el);
36511     return new Roo.XTemplate(el.value || el.innerHTML);
36512 };/*
36513  * Original code for Roojs - LGPL
36514  * <script type="text/javascript">
36515  */
36516  
36517 /**
36518  * @class Roo.XComponent
36519  * A delayed Element creator...
36520  * 
36521  * Mypart.xyx = new Roo.XComponent({
36522
36523     parent : 'Mypart.xyz', // empty == document.element.!!
36524     order : '001',
36525     name : 'xxxx'
36526     region : 'xxxx'
36527     disabled : function() {} 
36528      
36529     tree : function() { // return an tree of xtype declared components
36530         var MODULE = this;
36531         return 
36532         {
36533             xtype : 'NestedLayoutPanel',
36534             // technicall
36535         }
36536      ]
36537  *})
36538  * @extends Roo.util.Observable
36539  * @constructor
36540  * @param cfg {Object} configuration of component
36541  * 
36542  */
36543 Roo.XComponent = function(cfg) {
36544     Roo.apply(this, cfg);
36545     this.addEvents({ 
36546         /**
36547              * @event built
36548              * Fires when this the componnt is built
36549              * @param {Roo.XComponent} c the component
36550              */
36551         'built' : true,
36552         /**
36553              * @event buildcomplete
36554              * Fires on the top level element when all elements have been built
36555              * @param {Roo.XComponent} c the top level component.
36556          */
36557         'buildcomplete' : true
36558         
36559     });
36560     
36561     Roo.XComponent.register(this);
36562     this.modules = false;
36563     this.el = false; // where the layout goes..
36564     
36565     
36566 }
36567 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36568     /**
36569      * @property el
36570      * The created element (with Roo.factory())
36571      * @type {Roo.Layout}
36572      */
36573     el  : false,
36574     
36575     /**
36576      * @property el
36577      * for BC  - use el in new code
36578      * @type {Roo.Layout}
36579      */
36580     panel : false,
36581     
36582     /**
36583      * @property layout
36584      * for BC  - use el in new code
36585      * @type {Roo.Layout}
36586      */
36587     layout : false,
36588     
36589      /**
36590      * @cfg {Function|boolean} disabled
36591      * If this module is disabled by some rule, return true from the funtion
36592      */
36593     disabled : false,
36594     
36595     /**
36596      * @cfg {String} parent 
36597      * Name of parent element which it get xtype added to..
36598      */
36599     parent: false,
36600     
36601     /**
36602      * @cfg {String} order
36603      * Used to set the order in which elements are created (usefull for multiple tabs)
36604      */
36605     
36606     order : false,
36607     /**
36608      * @cfg {String} name
36609      * String to display while loading.
36610      */
36611     name : false,
36612     /**
36613      * @cfg {Array} items
36614      * A single item array - the first element is the root of the tree..
36615      * It's done this way to stay compatible with the Xtype system...
36616      */
36617     items : false
36618      
36619      
36620     
36621 });
36622
36623 Roo.apply(Roo.XComponent, {
36624     
36625     /**
36626      * @property  buildCompleted
36627      * True when the builder has completed building the interface.
36628      * @type Boolean
36629      */
36630     buildCompleted : false,
36631      
36632     /**
36633      * @property  topModule
36634      * the upper most module - uses document.element as it's constructor.
36635      * @type Object
36636      */
36637      
36638     topModule  : false,
36639       
36640     /**
36641      * @property  modules
36642      * array of modules to be created by registration system.
36643      * @type Roo.XComponent
36644      */
36645     
36646     modules : [],
36647       
36648     
36649     /**
36650      * Register components to be built later.
36651      *
36652      * This solves the following issues
36653      * - Building is not done on page load, but after an authentication process has occured.
36654      * - Interface elements are registered on page load
36655      * - Parent Interface elements may not be loaded before child, so this handles that..
36656      * 
36657      *
36658      * example:
36659      * 
36660      * MyApp.register({
36661           order : '000001',
36662           module : 'Pman.Tab.projectMgr',
36663           region : 'center',
36664           parent : 'Pman.layout',
36665           disabled : false,  // or use a function..
36666         })
36667      
36668      * * @param {Object} details about module
36669      */
36670     register : function(obj) {
36671         this.modules.push(obj);
36672          
36673     },
36674     /**
36675      * convert a string to an object..
36676      * 
36677      */
36678     
36679     toObject : function(str)
36680     {
36681         if (!str || typeof(str) == 'object') {
36682             return str;
36683         }
36684         var ar = str.split('.');
36685         var rt, o;
36686         rt = ar.shift();
36687             /** eval:var:o */
36688         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36689         if (o === false) {
36690             throw "Module not found : " + str;
36691         }
36692         Roo.each(ar, function(e) {
36693             if (typeof(o[e]) == 'undefined') {
36694                 throw "Module not found : " + str;
36695             }
36696             o = o[e];
36697         });
36698         return o;
36699         
36700     },
36701     
36702     
36703     /**
36704      * move modules into their correct place in the tree..
36705      * 
36706      */
36707     preBuild : function ()
36708     {
36709         
36710         Roo.each(this.modules , function (obj)
36711         {
36712             obj.parent = this.toObject(obj.parent);
36713             
36714             if (!obj.parent) {
36715                 this.topModule = obj;
36716                 return;
36717             }
36718             
36719             if (!obj.parent.modules) {
36720                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36721                     function(o) { return o.order + '' }
36722                 );
36723             }
36724             
36725             obj.parent.modules.add(obj);
36726         }, this);
36727     },
36728     
36729      /**
36730      * make a list of modules to build.
36731      * @return {Array} list of modules. 
36732      */ 
36733     
36734     buildOrder : function()
36735     {
36736         var _this = this;
36737         var cmp = function(a,b) {   
36738             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36739         };
36740         
36741         if (!this.topModule || !this.topModule.modules) {
36742             throw "No top level modules to build";
36743         }
36744        
36745         // make a flat list in order of modules to build.
36746         var mods = [ this.topModule ];
36747         
36748         
36749         // add modules to their parents..
36750         var addMod = function(m) {
36751            // Roo.debug && Roo.log(m.modKey);
36752             
36753             mods.push(m);
36754             if (m.modules) {
36755                 m.modules.keySort('ASC',  cmp );
36756                 m.modules.each(addMod);
36757             }
36758             // not sure if this is used any more..
36759             if (m.finalize) {
36760                 m.finalize.name = m.name + " (clean up) ";
36761                 mods.push(m.finalize);
36762             }
36763             
36764         }
36765         this.topModule.modules.keySort('ASC',  cmp );
36766         this.topModule.modules.each(addMod);
36767         return mods;
36768     },
36769     
36770      /**
36771      * Build the registered modules.
36772      * @param {Object} parent element.
36773      * @param {Function} optional method to call after module has been added.
36774      * 
36775      */ 
36776    
36777     build : function() 
36778     {
36779         
36780         this.preBuild();
36781         var mods = this.buildOrder();
36782       
36783         //this.allmods = mods;
36784         //Roo.debug && Roo.log(mods);
36785         //return;
36786         if (!mods.length) { // should not happen
36787             throw "NO modules!!!";
36788         }
36789         
36790         
36791         
36792         // flash it up as modal - so we store the mask!?
36793         Roo.MessageBox.show({ title: 'loading' });
36794         Roo.MessageBox.show({
36795            title: "Please wait...",
36796            msg: "Building Interface...",
36797            width:450,
36798            progress:true,
36799            closable:false,
36800            modal: false
36801           
36802         });
36803         var total = mods.length;
36804         
36805         var _this = this;
36806         var progressRun = function() {
36807             if (!mods.length) {
36808                 Roo.debug && Roo.log('hide?');
36809                 Roo.MessageBox.hide();
36810                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36811                 return;    
36812             }
36813             
36814             var m = mods.shift();
36815             Roo.debug && Roo.log(m);
36816             if (typeof(m) == 'function') { // not sure if this is supported any more..
36817                 m.call(this);
36818                 return progressRun.defer(10, _this);
36819             } 
36820             
36821             Roo.MessageBox.updateProgress(
36822                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36823                     " of " + total + 
36824                     (m.name ? (' - ' + m.name) : '')
36825                     );
36826             
36827          
36828             
36829             var disabled = (typeof(m.disabled) == 'function') ?
36830                 m.disabled.call(m.module.disabled) : m.disabled;    
36831             
36832             
36833             if (disabled) {
36834                 return progressRun(); // we do not update the display!
36835             }
36836             
36837             if (!m.parent) {
36838                 // it's a top level one..
36839                 var layoutbase = new Ext.BorderLayout(document.body, {
36840                
36841                     center: {
36842                          titlebar: false,
36843                          autoScroll:false,
36844                          closeOnTab: true,
36845                          tabPosition: 'top',
36846                          //resizeTabs: true,
36847                          alwaysShowTabs: true,
36848                          minTabWidth: 140
36849                     }
36850                 });
36851                 var tree = m.tree();
36852                 tree.region = 'center';
36853                 m.el = layoutbase.addxtype(tree);
36854                 m.panel = m.el;
36855                 m.layout = m.panel.layout;    
36856                 return progressRun.defer(10, _this);
36857             }
36858             
36859             var tree = m.tree();
36860             tree.region = tree.region || m.region;
36861             m.el = m.parent.el.addxtype(tree);
36862             m.fireEvent('built', m);
36863             m.panel = m.el;
36864             m.layout = m.panel.layout;    
36865             progressRun.defer(10, _this); 
36866             
36867         }
36868         progressRun.defer(1, _this);
36869      
36870         
36871         
36872     }
36873      
36874    
36875     
36876     
36877 });
36878  //<script type="text/javascript">
36879
36880
36881 /**
36882  * @class Roo.Login
36883  * @extends Roo.LayoutDialog
36884  * A generic Login Dialog..... - only one needed in theory!?!?
36885  *
36886  * Fires XComponent builder on success...
36887  * 
36888  * Sends 
36889  *    username,password, lang = for login actions.
36890  *    check = 1 for periodic checking that sesion is valid.
36891  *    passwordRequest = email request password
36892  *    logout = 1 = to logout
36893  * 
36894  * Affects: (this id="????" elements)
36895  *   loading  (removed) (used to indicate application is loading)
36896  *   loading-mask (hides) (used to hide application when it's building loading)
36897  *   
36898  * 
36899  * Usage: 
36900  *    
36901  * 
36902  * Myapp.login = Roo.Login({
36903      url: xxxx,
36904    
36905      realm : 'Myapp', 
36906      
36907      
36908      method : 'POST',
36909      
36910      
36911      * 
36912  })
36913  * 
36914  * 
36915  * 
36916  **/
36917  
36918 Roo.Login = function(cfg)
36919 {
36920     this.addEvents({
36921         'refreshed' : true
36922     });
36923     
36924     Roo.apply(this,cfg);
36925     
36926     Roo.onReady(function() {
36927         this.onLoad();
36928     }, this);
36929     // call parent..
36930     
36931    
36932     Roo.Login.superclass.constructor.call(this, this);
36933     //this.addxtype(this.items[0]);
36934     
36935     
36936 }
36937
36938
36939 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36940     
36941     /**
36942      * @cfg {String} method
36943      * Method used to query for login details.
36944      */
36945     
36946     method : 'POST',
36947     /**
36948      * @cfg {String} url
36949      * URL to query login data. - eg. baseURL + '/Login.php'
36950      */
36951     url : '',
36952     
36953     /**
36954      * @property user
36955      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36956      * @type {Object} 
36957      */
36958     user : false,
36959     /**
36960      * @property checkFails
36961      * Number of times we have attempted to get authentication check, and failed.
36962      * @type {Number} 
36963      */
36964     checkFails : 0,
36965       /**
36966      * @property intervalID
36967      * The window interval that does the constant login checking.
36968      * @type {Number} 
36969      */
36970     intervalID : 0,
36971     
36972     
36973     onLoad : function() // called on page load...
36974     {
36975         // load 
36976          
36977         if (Roo.get('loading')) { // clear any loading indicator..
36978             Roo.get('loading').remove();
36979         }
36980         
36981         //this.switchLang('en'); // set the language to english..
36982        
36983         this.check({
36984             success:  function(response, opts)  {  // check successfull...
36985             
36986                 var res = this.processResponse(response);
36987                 this.checkFails =0;
36988                 if (!res.success) { // error!
36989                     this.checkFails = 5;
36990                     //console.log('call failure');
36991                     return this.failure(response,opts);
36992                 }
36993                 
36994                 if (!res.data.id) { // id=0 == login failure.
36995                     return this.show();
36996                 }
36997                 
36998                               
36999                         //console.log(success);
37000                 this.fillAuth(res.data);   
37001                 this.checkFails =0;
37002                 Roo.XComponent.build();
37003             },
37004             failure : this.show
37005         });
37006         
37007     }, 
37008     
37009     
37010     check: function(cfg) // called every so often to refresh cookie etc..
37011     {
37012         if (cfg.again) { // could be undefined..
37013             this.checkFails++;
37014         } else {
37015             this.checkFails = 0;
37016         }
37017         var _this = this;
37018         if (this.sending) {
37019             if ( this.checkFails > 4) {
37020                 Roo.MessageBox.alert("Error",  
37021                     "Error getting authentication status. - try reloading, or wait a while", function() {
37022                         _this.sending = false;
37023                     }); 
37024                 return;
37025             }
37026             cfg.again = true;
37027             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37028             return;
37029         }
37030         this.sending = true;
37031         
37032         Roo.Ajax.request({  
37033             url: this.url,
37034             params: {
37035                 getAuthUser: true
37036             },  
37037             method: this.method,
37038             success:  cfg.success || this.success,
37039             failure : cfg.failure || this.failure,
37040             scope : this,
37041             callCfg : cfg
37042               
37043         });  
37044     }, 
37045     
37046     
37047     logout: function()
37048     {
37049         window.onbeforeunload = function() { }; // false does not work for IE..
37050         this.user = false;
37051         var _this = this;
37052         
37053         Roo.Ajax.request({  
37054             url: this.url,
37055             params: {
37056                 logout: 1
37057             },  
37058             method: 'GET',
37059             failure : function() {
37060                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37061                     document.location = document.location.toString() + '?ts=' + Math.random();
37062                 });
37063                 
37064             },
37065             success : function() {
37066                 _this.user = false;
37067                 this.checkFails =0;
37068                 // fixme..
37069                 document.location = document.location.toString() + '?ts=' + Math.random();
37070             }
37071               
37072               
37073         }); 
37074     },
37075     
37076     processResponse : function (response)
37077     {
37078         var res = '';
37079         try {
37080             res = Roo.decode(response.responseText);
37081             // oops...
37082             if (typeof(res) != 'object') {
37083                 res = { success : false, errorMsg : res, errors : true };
37084             }
37085             if (typeof(res.success) == 'undefined') {
37086                 res.success = false;
37087             }
37088             
37089         } catch(e) {
37090             res = { success : false,  errorMsg : response.responseText, errors : true };
37091         }
37092         return res;
37093     },
37094     
37095     success : function(response, opts)  // check successfull...
37096     {  
37097         this.sending = false;
37098         var res = this.processResponse(response);
37099         if (!res.success) {
37100             return this.failure(response, opts);
37101         }
37102         if (!res.data || !res.data.id) {
37103             return this.failure(response,opts);
37104         }
37105         //console.log(res);
37106         this.fillAuth(res.data);
37107         
37108         this.checkFails =0;
37109         
37110     },
37111     
37112     
37113     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37114     {
37115         this.authUser = -1;
37116         this.sending = false;
37117         var res = this.processResponse(response);
37118         //console.log(res);
37119         if ( this.checkFails > 2) {
37120         
37121             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37122                 "Error getting authentication status. - try reloading"); 
37123             return;
37124         }
37125         opts.callCfg.again = true;
37126         this.check.defer(1000, this, [ opts.callCfg ]);
37127         return;  
37128     },
37129     
37130     
37131     
37132     fillAuth: function(au) {
37133         this.startAuthCheck();
37134         this.authUserId = au.id;
37135         this.authUser = au;
37136         this.lastChecked = new Date();
37137         this.fireEvent('refreshed', au);
37138         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37139         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37140         au.lang = au.lang || 'en';
37141         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37142         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37143         this.switchLang(au.lang );
37144         
37145      
37146         // open system... - -on setyp..
37147         if (this.authUserId  < 0) {
37148             Roo.MessageBox.alert("Warning", 
37149                 "This is an open system - please set up a admin user with a password.");  
37150         }
37151          
37152         //Pman.onload(); // which should do nothing if it's a re-auth result...
37153         
37154              
37155     },
37156     
37157     startAuthCheck : function() // starter for timeout checking..
37158     {
37159         if (this.intervalID) { // timer already in place...
37160             return false;
37161         }
37162         var _this = this;
37163         this.intervalID =  window.setInterval(function() {
37164               _this.check(false);
37165             }, 120000); // every 120 secs = 2mins..
37166         
37167         
37168     },
37169          
37170     
37171     switchLang : function (lang) 
37172     {
37173         _T = typeof(_T) == 'undefined' ? false : _T;
37174           if (!_T || !lang.length) {
37175             return;
37176         }
37177         
37178         if (!_T && lang != 'en') {
37179             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37180             return;
37181         }
37182         
37183         if (typeof(_T.en) == 'undefined') {
37184             _T.en = {};
37185             Roo.apply(_T.en, _T);
37186         }
37187         
37188         if (typeof(_T[lang]) == 'undefined') {
37189             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37190             return;
37191         }
37192         
37193         
37194         Roo.apply(_T, _T[lang]);
37195         // just need to set the text values for everything...
37196         var _this = this;
37197         /* this will not work ...
37198         if (this.form) { 
37199             
37200                
37201             function formLabel(name, val) {
37202                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37203             }
37204             
37205             formLabel('password', "Password"+':');
37206             formLabel('username', "Email Address"+':');
37207             formLabel('lang', "Language"+':');
37208             this.dialog.setTitle("Login");
37209             this.dialog.buttons[0].setText("Forgot Password");
37210             this.dialog.buttons[1].setText("Login");
37211         }
37212         */
37213         
37214         
37215     },
37216     
37217     
37218     title: "Login",
37219     modal: true,
37220     width:  350,
37221     //height: 230,
37222     height: 180,
37223     shadow: true,
37224     minWidth:200,
37225     minHeight:180,
37226     //proxyDrag: true,
37227     closable: false,
37228     draggable: false,
37229     collapsible: false,
37230     resizable: false,
37231     center: {  // needed??
37232         autoScroll:false,
37233         titlebar: false,
37234        // tabPosition: 'top',
37235         hideTabs: true,
37236         closeOnTab: true,
37237         alwaysShowTabs: false
37238     } ,
37239     listeners : {
37240         
37241         show  : function(dlg)
37242         {
37243             //console.log(this);
37244             this.form = this.layout.getRegion('center').activePanel.form;
37245             this.form.dialog = dlg;
37246             this.buttons[0].form = this.form;
37247             this.buttons[0].dialog = dlg;
37248             this.buttons[1].form = this.form;
37249             this.buttons[1].dialog = dlg;
37250            
37251            //this.resizeToLogo.defer(1000,this);
37252             // this is all related to resizing for logos..
37253             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37254            //// if (!sz) {
37255              //   this.resizeToLogo.defer(1000,this);
37256              //   return;
37257            // }
37258             //var w = Ext.lib.Dom.getViewWidth() - 100;
37259             //var h = Ext.lib.Dom.getViewHeight() - 100;
37260             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37261             //this.center();
37262             if (this.disabled) {
37263                 this.hide();
37264                 return;
37265             }
37266             
37267             if (this.user.id < 0) { // used for inital setup situations.
37268                 return;
37269             }
37270             
37271             if (this.intervalID) {
37272                 // remove the timer
37273                 window.clearInterval(this.intervalID);
37274                 this.intervalID = false;
37275             }
37276             
37277             
37278             if (Roo.get('loading')) {
37279                 Roo.get('loading').remove();
37280             }
37281             if (Roo.get('loading-mask')) {
37282                 Roo.get('loading-mask').hide();
37283             }
37284             
37285             //incomming._node = tnode;
37286             this.form.reset();
37287             //this.dialog.modal = !modal;
37288             //this.dialog.show();
37289             this.el.unmask(); 
37290             
37291             
37292             this.form.setValues({
37293                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37294                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37295             });
37296             
37297             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37298             if (this.form.findField('username').getValue().length > 0 ){
37299                 this.form.findField('password').focus();
37300             } else {
37301                this.form.findField('username').focus();
37302             }
37303     
37304         }
37305     },
37306     items : [
37307          {
37308        
37309             xtype : 'ContentPanel',
37310             xns : Roo,
37311             region: 'center',
37312             fitToFrame : true,
37313             
37314             items : [
37315     
37316                 {
37317                
37318                     xtype : 'Form',
37319                     xns : Roo.form,
37320                     labelWidth: 100,
37321                     style : 'margin: 10px;',
37322                     
37323                     listeners : {
37324                         actionfailed : function(f, act) {
37325                             // form can return { errors: .... }
37326                                 
37327                             //act.result.errors // invalid form element list...
37328                             //act.result.errorMsg// invalid form element list...
37329                             
37330                             this.dialog.el.unmask();
37331                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37332                                         "Login failed - communication error - try again.");
37333                                       
37334                         },
37335                         actioncomplete: function(re, act) {
37336                              
37337                             Roo.state.Manager.set(
37338                                 this.dialog.realm + '.username',  
37339                                     this.findField('username').getValue()
37340                             );
37341                             Roo.state.Manager.set(
37342                                 this.dialog.realm + '.lang',  
37343                                 this.findField('lang').getValue() 
37344                             );
37345                             
37346                             this.dialog.fillAuth(act.result.data);
37347                               
37348                             this.dialog.hide();
37349                             
37350                             if (Roo.get('loading-mask')) {
37351                                 Roo.get('loading-mask').show();
37352                             }
37353                             Roo.XComponent.build();
37354                             
37355                              
37356                             
37357                         }
37358                     },
37359                     items : [
37360                         {
37361                             xtype : 'TextField',
37362                             xns : Roo.form,
37363                             fieldLabel: "Email Address",
37364                             name: 'username',
37365                             width:200,
37366                             autoCreate : {tag: "input", type: "text", size: "20"}
37367                         },
37368                         {
37369                             xtype : 'TextField',
37370                             xns : Roo.form,
37371                             fieldLabel: "Password",
37372                             inputType: 'password',
37373                             name: 'password',
37374                             width:200,
37375                             autoCreate : {tag: "input", type: "text", size: "20"},
37376                             listeners : {
37377                                 specialkey : function(e,ev) {
37378                                     if (ev.keyCode == 13) {
37379                                         this.form.dialog.el.mask("Logging in");
37380                                         this.form.doAction('submit', {
37381                                             url: this.form.dialog.url,
37382                                             method: this.form.dialog.method
37383                                         });
37384                                     }
37385                                 }
37386                             }  
37387                         },
37388                         {
37389                             xtype : 'ComboBox',
37390                             xns : Roo.form,
37391                             fieldLabel: "Language",
37392                             name : 'langdisp',
37393                             store: {
37394                                 xtype : 'SimpleStore',
37395                                 fields: ['lang', 'ldisp'],
37396                                 data : [
37397                                     [ 'en', 'English' ],
37398                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37399                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37400                                 ]
37401                             },
37402                             
37403                             valueField : 'lang',
37404                             hiddenName:  'lang',
37405                             width: 200,
37406                             displayField:'ldisp',
37407                             typeAhead: false,
37408                             editable: false,
37409                             mode: 'local',
37410                             triggerAction: 'all',
37411                             emptyText:'Select a Language...',
37412                             selectOnFocus:true,
37413                             listeners : {
37414                                 select :  function(cb, rec, ix) {
37415                                     this.form.switchLang(rec.data.lang);
37416                                 }
37417                             }
37418                         
37419                         }
37420                     ]
37421                 }
37422                   
37423                 
37424             ]
37425         }
37426     ],
37427     buttons : [
37428         {
37429             xtype : 'Button',
37430             xns : 'Roo',
37431             text : "Forgot Password",
37432             listeners : {
37433                 click : function() {
37434                     //console.log(this);
37435                     var n = this.form.findField('username').getValue();
37436                     if (!n.length) {
37437                         Roo.MessageBox.alert("Error", "Fill in your email address");
37438                         return;
37439                     }
37440                     Roo.Ajax.request({
37441                         url: this.dialog.url,
37442                         params: {
37443                             passwordRequest: n
37444                         },
37445                         method: this.dialog.method,
37446                         success:  function(response, opts)  {  // check successfull...
37447                         
37448                             var res = this.dialog.processResponse(response);
37449                             if (!res.success) { // error!
37450                                Roo.MessageBox.alert("Error" ,
37451                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37452                                return;
37453                             }
37454                             Roo.MessageBox.alert("Notice" ,
37455                                 "Please check you email for the Password Reset message");
37456                         },
37457                         failure : function() {
37458                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37459                         }
37460                         
37461                     });
37462                 }
37463             }
37464         },
37465         {
37466             xtype : 'Button',
37467             xns : 'Roo',
37468             text : "Login",
37469             listeners : {
37470                 
37471                 click : function () {
37472                         
37473                     this.dialog.el.mask("Logging in");
37474                     this.form.doAction('submit', {
37475                             url: this.dialog.url,
37476                             method: this.dialog.method
37477                     });
37478                 }
37479             }
37480         }
37481     ]
37482   
37483   
37484 })
37485  
37486
37487
37488