Roo/tree/TreeLoader.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157         /**
9158          * @event beforeclick
9159          * Fires before a click is processed. Returns false to cancel the default action.
9160          * @param {Roo.View} this
9161          * @param {Number} index The index of the target node
9162          * @param {HTMLElement} node The target node
9163          * @param {Roo.EventObject} e The raw event object
9164          */
9165             "beforeclick" : true,
9166         /**
9167          * @event click
9168          * Fires when a template node is clicked.
9169          * @param {Roo.View} this
9170          * @param {Number} index The index of the target node
9171          * @param {HTMLElement} node The target node
9172          * @param {Roo.EventObject} e The raw event object
9173          */
9174             "click" : true,
9175         /**
9176          * @event dblclick
9177          * Fires when a template node is double clicked.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "dblclick" : true,
9184         /**
9185          * @event contextmenu
9186          * Fires when a template node is right clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "contextmenu" : true,
9193         /**
9194          * @event selectionchange
9195          * Fires when the selected nodes change.
9196          * @param {Roo.View} this
9197          * @param {Array} selections Array of the selected nodes
9198          */
9199             "selectionchange" : true,
9200     
9201         /**
9202          * @event beforeselect
9203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204          * @param {Roo.View} this
9205          * @param {HTMLElement} node The node to be selected
9206          * @param {Array} selections Array of currently selected nodes
9207          */
9208             "beforeselect" : true,
9209         /**
9210          * @event preparedata
9211          * Fires on every row to render, to allow you to change the data.
9212          * @param {Roo.View} this
9213          * @param {Object} data to be rendered (change this)
9214          */
9215           "preparedata" : true
9216         });
9217
9218     this.el.on({
9219         "click": this.onClick,
9220         "dblclick": this.onDblClick,
9221         "contextmenu": this.onContextMenu,
9222         scope:this
9223     });
9224
9225     this.selections = [];
9226     this.nodes = [];
9227     this.cmp = new Roo.CompositeElementLite([]);
9228     if(this.store){
9229         this.store = Roo.factory(this.store, Roo.data);
9230         this.setStore(this.store, true);
9231     }
9232     Roo.View.superclass.constructor.call(this);
9233 };
9234
9235 Roo.extend(Roo.View, Roo.util.Observable, {
9236     
9237      /**
9238      * @cfg {Roo.data.Store} store Data store to load data from.
9239      */
9240     store : false,
9241     
9242     /**
9243      * @cfg {String|Roo.Element} el The container element.
9244      */
9245     el : '',
9246     
9247     /**
9248      * @cfg {String|Roo.Template} tpl The template used by this View 
9249      */
9250     tpl : false,
9251     
9252     /**
9253      * @cfg {String} selectedClass The css class to add to selected nodes
9254      */
9255     selectedClass : "x-view-selected",
9256      /**
9257      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9258      */
9259     emptyText : "",
9260     /**
9261      * @cfg {Boolean} multiSelect Allow multiple selection
9262      */
9263     
9264     multiSelect : false,
9265     /**
9266      * @cfg {Boolean} singleSelect Allow single selection
9267      */
9268     singleSelect:  false,
9269     
9270     /**
9271      * Returns the element this view is bound to.
9272      * @return {Roo.Element}
9273      */
9274     getEl : function(){
9275         return this.el;
9276     },
9277
9278     /**
9279      * Refreshes the view.
9280      */
9281     refresh : function(){
9282         var t = this.tpl;
9283         this.clearSelections();
9284         this.el.update("");
9285         var html = [];
9286         var records = this.store.getRange();
9287         if(records.length < 1){
9288             this.el.update(this.emptyText);
9289             return;
9290         }
9291         for(var i = 0, len = records.length; i < len; i++){
9292             var data = this.prepareData(records[i].data, i, records[i]);
9293             this.fireEvent("preparedata", this, data, i, records[i]);
9294             html[html.length] = t.apply(data);
9295         }
9296         this.el.update(html.join(""));
9297         this.nodes = this.el.dom.childNodes;
9298         this.updateIndexes(0);
9299     },
9300
9301     /**
9302      * Function to override to reformat the data that is sent to
9303      * the template for each node.
9304      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9305      * a JSON object for an UpdateManager bound view).
9306      */
9307     prepareData : function(data){
9308         return data;
9309     },
9310
9311     onUpdate : function(ds, record){
9312         this.clearSelections();
9313         var index = this.store.indexOf(record);
9314         var n = this.nodes[index];
9315         this.tpl.insertBefore(n, this.prepareData(record.data));
9316         n.parentNode.removeChild(n);
9317         this.updateIndexes(index, index);
9318     },
9319
9320     onAdd : function(ds, records, index){
9321         this.clearSelections();
9322         if(this.nodes.length == 0){
9323             this.refresh();
9324             return;
9325         }
9326         var n = this.nodes[index];
9327         for(var i = 0, len = records.length; i < len; i++){
9328             var d = this.prepareData(records[i].data);
9329             if(n){
9330                 this.tpl.insertBefore(n, d);
9331             }else{
9332                 this.tpl.append(this.el, d);
9333             }
9334         }
9335         this.updateIndexes(index);
9336     },
9337
9338     onRemove : function(ds, record, index){
9339         this.clearSelections();
9340         this.el.dom.removeChild(this.nodes[index]);
9341         this.updateIndexes(index);
9342     },
9343
9344     /**
9345      * Refresh an individual node.
9346      * @param {Number} index
9347      */
9348     refreshNode : function(index){
9349         this.onUpdate(this.store, this.store.getAt(index));
9350     },
9351
9352     updateIndexes : function(startIndex, endIndex){
9353         var ns = this.nodes;
9354         startIndex = startIndex || 0;
9355         endIndex = endIndex || ns.length - 1;
9356         for(var i = startIndex; i <= endIndex; i++){
9357             ns[i].nodeIndex = i;
9358         }
9359     },
9360
9361     /**
9362      * Changes the data store this view uses and refresh the view.
9363      * @param {Store} store
9364      */
9365     setStore : function(store, initial){
9366         if(!initial && this.store){
9367             this.store.un("datachanged", this.refresh);
9368             this.store.un("add", this.onAdd);
9369             this.store.un("remove", this.onRemove);
9370             this.store.un("update", this.onUpdate);
9371             this.store.un("clear", this.refresh);
9372         }
9373         if(store){
9374           
9375             store.on("datachanged", this.refresh, this);
9376             store.on("add", this.onAdd, this);
9377             store.on("remove", this.onRemove, this);
9378             store.on("update", this.onUpdate, this);
9379             store.on("clear", this.refresh, this);
9380         }
9381         
9382         if(store){
9383             this.refresh();
9384         }
9385     },
9386
9387     /**
9388      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9389      * @param {HTMLElement} node
9390      * @return {HTMLElement} The template node
9391      */
9392     findItemFromChild : function(node){
9393         var el = this.el.dom;
9394         if(!node || node.parentNode == el){
9395                     return node;
9396             }
9397             var p = node.parentNode;
9398             while(p && p != el){
9399             if(p.parentNode == el){
9400                 return p;
9401             }
9402             p = p.parentNode;
9403         }
9404             return null;
9405     },
9406
9407     /** @ignore */
9408     onClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             var index = this.indexOf(item);
9412             if(this.onItemClick(item, index, e) !== false){
9413                 this.fireEvent("click", this, index, item, e);
9414             }
9415         }else{
9416             this.clearSelections();
9417         }
9418     },
9419
9420     /** @ignore */
9421     onContextMenu : function(e){
9422         var item = this.findItemFromChild(e.getTarget());
9423         if(item){
9424             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9425         }
9426     },
9427
9428     /** @ignore */
9429     onDblClick : function(e){
9430         var item = this.findItemFromChild(e.getTarget());
9431         if(item){
9432             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9433         }
9434     },
9435
9436     onItemClick : function(item, index, e){
9437         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9438             return false;
9439         }
9440         if(this.multiSelect || this.singleSelect){
9441             if(this.multiSelect && e.shiftKey && this.lastSelection){
9442                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9443             }else{
9444                 this.select(item, this.multiSelect && e.ctrlKey);
9445                 this.lastSelection = item;
9446             }
9447             e.preventDefault();
9448         }
9449         return true;
9450     },
9451
9452     /**
9453      * Get the number of selected nodes.
9454      * @return {Number}
9455      */
9456     getSelectionCount : function(){
9457         return this.selections.length;
9458     },
9459
9460     /**
9461      * Get the currently selected nodes.
9462      * @return {Array} An array of HTMLElements
9463      */
9464     getSelectedNodes : function(){
9465         return this.selections;
9466     },
9467
9468     /**
9469      * Get the indexes of the selected nodes.
9470      * @return {Array}
9471      */
9472     getSelectedIndexes : function(){
9473         var indexes = [], s = this.selections;
9474         for(var i = 0, len = s.length; i < len; i++){
9475             indexes.push(s[i].nodeIndex);
9476         }
9477         return indexes;
9478     },
9479
9480     /**
9481      * Clear all selections
9482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9483      */
9484     clearSelections : function(suppressEvent){
9485         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9486             this.cmp.elements = this.selections;
9487             this.cmp.removeClass(this.selectedClass);
9488             this.selections = [];
9489             if(!suppressEvent){
9490                 this.fireEvent("selectionchange", this, this.selections);
9491             }
9492         }
9493     },
9494
9495     /**
9496      * Returns true if the passed node is selected
9497      * @param {HTMLElement/Number} node The node or node index
9498      * @return {Boolean}
9499      */
9500     isSelected : function(node){
9501         var s = this.selections;
9502         if(s.length < 1){
9503             return false;
9504         }
9505         node = this.getNode(node);
9506         return s.indexOf(node) !== -1;
9507     },
9508
9509     /**
9510      * Selects nodes.
9511      * @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
9512      * @param {Boolean} keepExisting (optional) true to keep existing selections
9513      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9514      */
9515     select : function(nodeInfo, keepExisting, suppressEvent){
9516         if(nodeInfo instanceof Array){
9517             if(!keepExisting){
9518                 this.clearSelections(true);
9519             }
9520             for(var i = 0, len = nodeInfo.length; i < len; i++){
9521                 this.select(nodeInfo[i], true, true);
9522             }
9523         } else{
9524             var node = this.getNode(nodeInfo);
9525             if(node && !this.isSelected(node)){
9526                 if(!keepExisting){
9527                     this.clearSelections(true);
9528                 }
9529                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9530                     Roo.fly(node).addClass(this.selectedClass);
9531                     this.selections.push(node);
9532                     if(!suppressEvent){
9533                         this.fireEvent("selectionchange", this, this.selections);
9534                     }
9535                 }
9536             }
9537         }
9538     },
9539
9540     /**
9541      * Gets a template node.
9542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9543      * @return {HTMLElement} The node or null if it wasn't found
9544      */
9545     getNode : function(nodeInfo){
9546         if(typeof nodeInfo == "string"){
9547             return document.getElementById(nodeInfo);
9548         }else if(typeof nodeInfo == "number"){
9549             return this.nodes[nodeInfo];
9550         }
9551         return nodeInfo;
9552     },
9553
9554     /**
9555      * Gets a range template nodes.
9556      * @param {Number} startIndex
9557      * @param {Number} endIndex
9558      * @return {Array} An array of nodes
9559      */
9560     getNodes : function(start, end){
9561         var ns = this.nodes;
9562         start = start || 0;
9563         end = typeof end == "undefined" ? ns.length - 1 : end;
9564         var nodes = [];
9565         if(start <= end){
9566             for(var i = start; i <= end; i++){
9567                 nodes.push(ns[i]);
9568             }
9569         } else{
9570             for(var i = start; i >= end; i--){
9571                 nodes.push(ns[i]);
9572             }
9573         }
9574         return nodes;
9575     },
9576
9577     /**
9578      * Finds the index of the passed node
9579      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9580      * @return {Number} The index of the node or -1
9581      */
9582     indexOf : function(node){
9583         node = this.getNode(node);
9584         if(typeof node.nodeIndex == "number"){
9585             return node.nodeIndex;
9586         }
9587         var ns = this.nodes;
9588         for(var i = 0, len = ns.length; i < len; i++){
9589             if(ns[i] == node){
9590                 return i;
9591             }
9592         }
9593         return -1;
9594     }
9595 });
9596 /*
9597  * Based on:
9598  * Ext JS Library 1.1.1
9599  * Copyright(c) 2006-2007, Ext JS, LLC.
9600  *
9601  * Originally Released Under LGPL - original licence link has changed is not relivant.
9602  *
9603  * Fork - LGPL
9604  * <script type="text/javascript">
9605  */
9606
9607 /**
9608  * @class Roo.JsonView
9609  * @extends Roo.View
9610  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9611 <pre><code>
9612 var view = new Roo.JsonView({
9613     container: "my-element",
9614     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9615     multiSelect: true, 
9616     jsonRoot: "data" 
9617 });
9618
9619 // listen for node click?
9620 view.on("click", function(vw, index, node, e){
9621     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9622 });
9623
9624 // direct load of JSON data
9625 view.load("foobar.php");
9626
9627 // Example from my blog list
9628 var tpl = new Roo.Template(
9629     '&lt;div class="entry"&gt;' +
9630     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9631     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9632     "&lt;/div&gt;&lt;hr /&gt;"
9633 );
9634
9635 var moreView = new Roo.JsonView({
9636     container :  "entry-list", 
9637     template : tpl,
9638     jsonRoot: "posts"
9639 });
9640 moreView.on("beforerender", this.sortEntries, this);
9641 moreView.load({
9642     url: "/blog/get-posts.php",
9643     params: "allposts=true",
9644     text: "Loading Blog Entries..."
9645 });
9646 </code></pre>
9647
9648 * Note: old code is supported with arguments : (container, template, config)
9649
9650
9651  * @constructor
9652  * Create a new JsonView
9653  * 
9654  * @param {Object} config The config object
9655  * 
9656  */
9657 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9658     
9659     
9660     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9661
9662     var um = this.el.getUpdateManager();
9663     um.setRenderer(this);
9664     um.on("update", this.onLoad, this);
9665     um.on("failure", this.onLoadException, this);
9666
9667     /**
9668      * @event beforerender
9669      * Fires before rendering of the downloaded JSON data.
9670      * @param {Roo.JsonView} this
9671      * @param {Object} data The JSON data loaded
9672      */
9673     /**
9674      * @event load
9675      * Fires when data is loaded.
9676      * @param {Roo.JsonView} this
9677      * @param {Object} data The JSON data loaded
9678      * @param {Object} response The raw Connect response object
9679      */
9680     /**
9681      * @event loadexception
9682      * Fires when loading fails.
9683      * @param {Roo.JsonView} this
9684      * @param {Object} response The raw Connect response object
9685      */
9686     this.addEvents({
9687         'beforerender' : true,
9688         'load' : true,
9689         'loadexception' : true
9690     });
9691 };
9692 Roo.extend(Roo.JsonView, Roo.View, {
9693     /**
9694      * @type {String} The root property in the loaded JSON object that contains the data
9695      */
9696     jsonRoot : "",
9697
9698     /**
9699      * Refreshes the view.
9700      */
9701     refresh : function(){
9702         this.clearSelections();
9703         this.el.update("");
9704         var html = [];
9705         var o = this.jsonData;
9706         if(o && o.length > 0){
9707             for(var i = 0, len = o.length; i < len; i++){
9708                 var data = this.prepareData(o[i], i, o);
9709                 html[html.length] = this.tpl.apply(data);
9710             }
9711         }else{
9712             html.push(this.emptyText);
9713         }
9714         this.el.update(html.join(""));
9715         this.nodes = this.el.dom.childNodes;
9716         this.updateIndexes(0);
9717     },
9718
9719     /**
9720      * 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.
9721      * @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:
9722      <pre><code>
9723      view.load({
9724          url: "your-url.php",
9725          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9726          callback: yourFunction,
9727          scope: yourObject, //(optional scope)
9728          discardUrl: false,
9729          nocache: false,
9730          text: "Loading...",
9731          timeout: 30,
9732          scripts: false
9733      });
9734      </code></pre>
9735      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9736      * 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.
9737      * @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}
9738      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9739      * @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.
9740      */
9741     load : function(){
9742         var um = this.el.getUpdateManager();
9743         um.update.apply(um, arguments);
9744     },
9745
9746     render : function(el, response){
9747         this.clearSelections();
9748         this.el.update("");
9749         var o;
9750         try{
9751             o = Roo.util.JSON.decode(response.responseText);
9752             if(this.jsonRoot){
9753                 
9754                 o = o[this.jsonRoot];
9755             }
9756         } catch(e){
9757         }
9758         /**
9759          * The current JSON data or null
9760          */
9761         this.jsonData = o;
9762         this.beforeRender();
9763         this.refresh();
9764     },
9765
9766 /**
9767  * Get the number of records in the current JSON dataset
9768  * @return {Number}
9769  */
9770     getCount : function(){
9771         return this.jsonData ? this.jsonData.length : 0;
9772     },
9773
9774 /**
9775  * Returns the JSON object for the specified node(s)
9776  * @param {HTMLElement/Array} node The node or an array of nodes
9777  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9778  * you get the JSON object for the node
9779  */
9780     getNodeData : function(node){
9781         if(node instanceof Array){
9782             var data = [];
9783             for(var i = 0, len = node.length; i < len; i++){
9784                 data.push(this.getNodeData(node[i]));
9785             }
9786             return data;
9787         }
9788         return this.jsonData[this.indexOf(node)] || null;
9789     },
9790
9791     beforeRender : function(){
9792         this.snapshot = this.jsonData;
9793         if(this.sortInfo){
9794             this.sort.apply(this, this.sortInfo);
9795         }
9796         this.fireEvent("beforerender", this, this.jsonData);
9797     },
9798
9799     onLoad : function(el, o){
9800         this.fireEvent("load", this, this.jsonData, o);
9801     },
9802
9803     onLoadException : function(el, o){
9804         this.fireEvent("loadexception", this, o);
9805     },
9806
9807 /**
9808  * Filter the data by a specific property.
9809  * @param {String} property A property on your JSON objects
9810  * @param {String/RegExp} value Either string that the property values
9811  * should start with, or a RegExp to test against the property
9812  */
9813     filter : function(property, value){
9814         if(this.jsonData){
9815             var data = [];
9816             var ss = this.snapshot;
9817             if(typeof value == "string"){
9818                 var vlen = value.length;
9819                 if(vlen == 0){
9820                     this.clearFilter();
9821                     return;
9822                 }
9823                 value = value.toLowerCase();
9824                 for(var i = 0, len = ss.length; i < len; i++){
9825                     var o = ss[i];
9826                     if(o[property].substr(0, vlen).toLowerCase() == value){
9827                         data.push(o);
9828                     }
9829                 }
9830             } else if(value.exec){ // regex?
9831                 for(var i = 0, len = ss.length; i < len; i++){
9832                     var o = ss[i];
9833                     if(value.test(o[property])){
9834                         data.push(o);
9835                     }
9836                 }
9837             } else{
9838                 return;
9839             }
9840             this.jsonData = data;
9841             this.refresh();
9842         }
9843     },
9844
9845 /**
9846  * Filter by a function. The passed function will be called with each
9847  * object in the current dataset. If the function returns true the value is kept,
9848  * otherwise it is filtered.
9849  * @param {Function} fn
9850  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9851  */
9852     filterBy : function(fn, scope){
9853         if(this.jsonData){
9854             var data = [];
9855             var ss = this.snapshot;
9856             for(var i = 0, len = ss.length; i < len; i++){
9857                 var o = ss[i];
9858                 if(fn.call(scope || this, o)){
9859                     data.push(o);
9860                 }
9861             }
9862             this.jsonData = data;
9863             this.refresh();
9864         }
9865     },
9866
9867 /**
9868  * Clears the current filter.
9869  */
9870     clearFilter : function(){
9871         if(this.snapshot && this.jsonData != this.snapshot){
9872             this.jsonData = this.snapshot;
9873             this.refresh();
9874         }
9875     },
9876
9877
9878 /**
9879  * Sorts the data for this view and refreshes it.
9880  * @param {String} property A property on your JSON objects to sort on
9881  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9882  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9883  */
9884     sort : function(property, dir, sortType){
9885         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9886         if(this.jsonData){
9887             var p = property;
9888             var dsc = dir && dir.toLowerCase() == "desc";
9889             var f = function(o1, o2){
9890                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9891                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9892                 ;
9893                 if(v1 < v2){
9894                     return dsc ? +1 : -1;
9895                 } else if(v1 > v2){
9896                     return dsc ? -1 : +1;
9897                 } else{
9898                     return 0;
9899                 }
9900             };
9901             this.jsonData.sort(f);
9902             this.refresh();
9903             if(this.jsonData != this.snapshot){
9904                 this.snapshot.sort(f);
9905             }
9906         }
9907     }
9908 });/*
9909  * Based on:
9910  * Ext JS Library 1.1.1
9911  * Copyright(c) 2006-2007, Ext JS, LLC.
9912  *
9913  * Originally Released Under LGPL - original licence link has changed is not relivant.
9914  *
9915  * Fork - LGPL
9916  * <script type="text/javascript">
9917  */
9918  
9919
9920 /**
9921  * @class Roo.ColorPalette
9922  * @extends Roo.Component
9923  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9924  * Here's an example of typical usage:
9925  * <pre><code>
9926 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9927 cp.render('my-div');
9928
9929 cp.on('select', function(palette, selColor){
9930     // do something with selColor
9931 });
9932 </code></pre>
9933  * @constructor
9934  * Create a new ColorPalette
9935  * @param {Object} config The config object
9936  */
9937 Roo.ColorPalette = function(config){
9938     Roo.ColorPalette.superclass.constructor.call(this, config);
9939     this.addEvents({
9940         /**
9941              * @event select
9942              * Fires when a color is selected
9943              * @param {ColorPalette} this
9944              * @param {String} color The 6-digit color hex code (without the # symbol)
9945              */
9946         select: true
9947     });
9948
9949     if(this.handler){
9950         this.on("select", this.handler, this.scope, true);
9951     }
9952 };
9953 Roo.extend(Roo.ColorPalette, Roo.Component, {
9954     /**
9955      * @cfg {String} itemCls
9956      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9957      */
9958     itemCls : "x-color-palette",
9959     /**
9960      * @cfg {String} value
9961      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9962      * the hex codes are case-sensitive.
9963      */
9964     value : null,
9965     clickEvent:'click',
9966     // private
9967     ctype: "Roo.ColorPalette",
9968
9969     /**
9970      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9971      */
9972     allowReselect : false,
9973
9974     /**
9975      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9976      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9977      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9978      * of colors with the width setting until the box is symmetrical.</p>
9979      * <p>You can override individual colors if needed:</p>
9980      * <pre><code>
9981 var cp = new Roo.ColorPalette();
9982 cp.colors[0] = "FF0000";  // change the first box to red
9983 </code></pre>
9984
9985 Or you can provide a custom array of your own for complete control:
9986 <pre><code>
9987 var cp = new Roo.ColorPalette();
9988 cp.colors = ["000000", "993300", "333300"];
9989 </code></pre>
9990      * @type Array
9991      */
9992     colors : [
9993         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9994         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9995         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9996         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9997         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9998     ],
9999
10000     // private
10001     onRender : function(container, position){
10002         var t = new Roo.MasterTemplate(
10003             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10004         );
10005         var c = this.colors;
10006         for(var i = 0, len = c.length; i < len; i++){
10007             t.add([c[i]]);
10008         }
10009         var el = document.createElement("div");
10010         el.className = this.itemCls;
10011         t.overwrite(el);
10012         container.dom.insertBefore(el, position);
10013         this.el = Roo.get(el);
10014         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10015         if(this.clickEvent != 'click'){
10016             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10017         }
10018     },
10019
10020     // private
10021     afterRender : function(){
10022         Roo.ColorPalette.superclass.afterRender.call(this);
10023         if(this.value){
10024             var s = this.value;
10025             this.value = null;
10026             this.select(s);
10027         }
10028     },
10029
10030     // private
10031     handleClick : function(e, t){
10032         e.preventDefault();
10033         if(!this.disabled){
10034             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10035             this.select(c.toUpperCase());
10036         }
10037     },
10038
10039     /**
10040      * Selects the specified color in the palette (fires the select event)
10041      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10042      */
10043     select : function(color){
10044         color = color.replace("#", "");
10045         if(color != this.value || this.allowReselect){
10046             var el = this.el;
10047             if(this.value){
10048                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10049             }
10050             el.child("a.color-"+color).addClass("x-color-palette-sel");
10051             this.value = color;
10052             this.fireEvent("select", this, color);
10053         }
10054     }
10055 });/*
10056  * Based on:
10057  * Ext JS Library 1.1.1
10058  * Copyright(c) 2006-2007, Ext JS, LLC.
10059  *
10060  * Originally Released Under LGPL - original licence link has changed is not relivant.
10061  *
10062  * Fork - LGPL
10063  * <script type="text/javascript">
10064  */
10065  
10066 /**
10067  * @class Roo.DatePicker
10068  * @extends Roo.Component
10069  * Simple date picker class.
10070  * @constructor
10071  * Create a new DatePicker
10072  * @param {Object} config The config object
10073  */
10074 Roo.DatePicker = function(config){
10075     Roo.DatePicker.superclass.constructor.call(this, config);
10076
10077     this.value = config && config.value ?
10078                  config.value.clearTime() : new Date().clearTime();
10079
10080     this.addEvents({
10081         /**
10082              * @event select
10083              * Fires when a date is selected
10084              * @param {DatePicker} this
10085              * @param {Date} date The selected date
10086              */
10087         select: true
10088     });
10089
10090     if(this.handler){
10091         this.on("select", this.handler,  this.scope || this);
10092     }
10093     // build the disabledDatesRE
10094     if(!this.disabledDatesRE && this.disabledDates){
10095         var dd = this.disabledDates;
10096         var re = "(?:";
10097         for(var i = 0; i < dd.length; i++){
10098             re += dd[i];
10099             if(i != dd.length-1) re += "|";
10100         }
10101         this.disabledDatesRE = new RegExp(re + ")");
10102     }
10103 };
10104
10105 Roo.extend(Roo.DatePicker, Roo.Component, {
10106     /**
10107      * @cfg {String} todayText
10108      * The text to display on the button that selects the current date (defaults to "Today")
10109      */
10110     todayText : "Today",
10111     /**
10112      * @cfg {String} okText
10113      * The text to display on the ok button
10114      */
10115     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10116     /**
10117      * @cfg {String} cancelText
10118      * The text to display on the cancel button
10119      */
10120     cancelText : "Cancel",
10121     /**
10122      * @cfg {String} todayTip
10123      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10124      */
10125     todayTip : "{0} (Spacebar)",
10126     /**
10127      * @cfg {Date} minDate
10128      * Minimum allowable date (JavaScript date object, defaults to null)
10129      */
10130     minDate : null,
10131     /**
10132      * @cfg {Date} maxDate
10133      * Maximum allowable date (JavaScript date object, defaults to null)
10134      */
10135     maxDate : null,
10136     /**
10137      * @cfg {String} minText
10138      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10139      */
10140     minText : "This date is before the minimum date",
10141     /**
10142      * @cfg {String} maxText
10143      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10144      */
10145     maxText : "This date is after the maximum date",
10146     /**
10147      * @cfg {String} format
10148      * The default date format string which can be overriden for localization support.  The format must be
10149      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10150      */
10151     format : "m/d/y",
10152     /**
10153      * @cfg {Array} disabledDays
10154      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10155      */
10156     disabledDays : null,
10157     /**
10158      * @cfg {String} disabledDaysText
10159      * The tooltip to display when the date falls on a disabled day (defaults to "")
10160      */
10161     disabledDaysText : "",
10162     /**
10163      * @cfg {RegExp} disabledDatesRE
10164      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10165      */
10166     disabledDatesRE : null,
10167     /**
10168      * @cfg {String} disabledDatesText
10169      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10170      */
10171     disabledDatesText : "",
10172     /**
10173      * @cfg {Boolean} constrainToViewport
10174      * True to constrain the date picker to the viewport (defaults to true)
10175      */
10176     constrainToViewport : true,
10177     /**
10178      * @cfg {Array} monthNames
10179      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10180      */
10181     monthNames : Date.monthNames,
10182     /**
10183      * @cfg {Array} dayNames
10184      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10185      */
10186     dayNames : Date.dayNames,
10187     /**
10188      * @cfg {String} nextText
10189      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10190      */
10191     nextText: 'Next Month (Control+Right)',
10192     /**
10193      * @cfg {String} prevText
10194      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10195      */
10196     prevText: 'Previous Month (Control+Left)',
10197     /**
10198      * @cfg {String} monthYearText
10199      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10200      */
10201     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10202     /**
10203      * @cfg {Number} startDay
10204      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10205      */
10206     startDay : 0,
10207     /**
10208      * @cfg {Bool} showClear
10209      * Show a clear button (usefull for date form elements that can be blank.)
10210      */
10211     
10212     showClear: false,
10213     
10214     /**
10215      * Sets the value of the date field
10216      * @param {Date} value The date to set
10217      */
10218     setValue : function(value){
10219         var old = this.value;
10220         this.value = value.clearTime(true);
10221         if(this.el){
10222             this.update(this.value);
10223         }
10224     },
10225
10226     /**
10227      * Gets the current selected value of the date field
10228      * @return {Date} The selected date
10229      */
10230     getValue : function(){
10231         return this.value;
10232     },
10233
10234     // private
10235     focus : function(){
10236         if(this.el){
10237             this.update(this.activeDate);
10238         }
10239     },
10240
10241     // private
10242     onRender : function(container, position){
10243         var m = [
10244              '<table cellspacing="0">',
10245                 '<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>',
10246                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10247         var dn = this.dayNames;
10248         for(var i = 0; i < 7; i++){
10249             var d = this.startDay+i;
10250             if(d > 6){
10251                 d = d-7;
10252             }
10253             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10254         }
10255         m[m.length] = "</tr></thead><tbody><tr>";
10256         for(var i = 0; i < 42; i++) {
10257             if(i % 7 == 0 && i != 0){
10258                 m[m.length] = "</tr><tr>";
10259             }
10260             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10261         }
10262         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10263             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10264
10265         var el = document.createElement("div");
10266         el.className = "x-date-picker";
10267         el.innerHTML = m.join("");
10268
10269         container.dom.insertBefore(el, position);
10270
10271         this.el = Roo.get(el);
10272         this.eventEl = Roo.get(el.firstChild);
10273
10274         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10275             handler: this.showPrevMonth,
10276             scope: this,
10277             preventDefault:true,
10278             stopDefault:true
10279         });
10280
10281         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10282             handler: this.showNextMonth,
10283             scope: this,
10284             preventDefault:true,
10285             stopDefault:true
10286         });
10287
10288         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10289
10290         this.monthPicker = this.el.down('div.x-date-mp');
10291         this.monthPicker.enableDisplayMode('block');
10292         
10293         var kn = new Roo.KeyNav(this.eventEl, {
10294             "left" : function(e){
10295                 e.ctrlKey ?
10296                     this.showPrevMonth() :
10297                     this.update(this.activeDate.add("d", -1));
10298             },
10299
10300             "right" : function(e){
10301                 e.ctrlKey ?
10302                     this.showNextMonth() :
10303                     this.update(this.activeDate.add("d", 1));
10304             },
10305
10306             "up" : function(e){
10307                 e.ctrlKey ?
10308                     this.showNextYear() :
10309                     this.update(this.activeDate.add("d", -7));
10310             },
10311
10312             "down" : function(e){
10313                 e.ctrlKey ?
10314                     this.showPrevYear() :
10315                     this.update(this.activeDate.add("d", 7));
10316             },
10317
10318             "pageUp" : function(e){
10319                 this.showNextMonth();
10320             },
10321
10322             "pageDown" : function(e){
10323                 this.showPrevMonth();
10324             },
10325
10326             "enter" : function(e){
10327                 e.stopPropagation();
10328                 return true;
10329             },
10330
10331             scope : this
10332         });
10333
10334         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10335
10336         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10337
10338         this.el.unselectable();
10339         
10340         this.cells = this.el.select("table.x-date-inner tbody td");
10341         this.textNodes = this.el.query("table.x-date-inner tbody span");
10342
10343         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10344             text: "&#160;",
10345             tooltip: this.monthYearText
10346         });
10347
10348         this.mbtn.on('click', this.showMonthPicker, this);
10349         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10350
10351
10352         var today = (new Date()).dateFormat(this.format);
10353         
10354         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10355         if (this.showClear) {
10356             baseTb.add( new Roo.Toolbar.Fill());
10357         }
10358         baseTb.add({
10359             text: String.format(this.todayText, today),
10360             tooltip: String.format(this.todayTip, today),
10361             handler: this.selectToday,
10362             scope: this
10363         });
10364         
10365         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10366             
10367         //});
10368         if (this.showClear) {
10369             
10370             baseTb.add( new Roo.Toolbar.Fill());
10371             baseTb.add({
10372                 text: '&#160;',
10373                 cls: 'x-btn-icon x-btn-clear',
10374                 handler: function() {
10375                     //this.value = '';
10376                     this.fireEvent("select", this, '');
10377                 },
10378                 scope: this
10379             });
10380         }
10381         
10382         
10383         if(Roo.isIE){
10384             this.el.repaint();
10385         }
10386         this.update(this.value);
10387     },
10388
10389     createMonthPicker : function(){
10390         if(!this.monthPicker.dom.firstChild){
10391             var buf = ['<table border="0" cellspacing="0">'];
10392             for(var i = 0; i < 6; i++){
10393                 buf.push(
10394                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10395                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10396                     i == 0 ?
10397                     '<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>' :
10398                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10399                 );
10400             }
10401             buf.push(
10402                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10403                     this.okText,
10404                     '</button><button type="button" class="x-date-mp-cancel">',
10405                     this.cancelText,
10406                     '</button></td></tr>',
10407                 '</table>'
10408             );
10409             this.monthPicker.update(buf.join(''));
10410             this.monthPicker.on('click', this.onMonthClick, this);
10411             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10412
10413             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10414             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10415
10416             this.mpMonths.each(function(m, a, i){
10417                 i += 1;
10418                 if((i%2) == 0){
10419                     m.dom.xmonth = 5 + Math.round(i * .5);
10420                 }else{
10421                     m.dom.xmonth = Math.round((i-1) * .5);
10422                 }
10423             });
10424         }
10425     },
10426
10427     showMonthPicker : function(){
10428         this.createMonthPicker();
10429         var size = this.el.getSize();
10430         this.monthPicker.setSize(size);
10431         this.monthPicker.child('table').setSize(size);
10432
10433         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10434         this.updateMPMonth(this.mpSelMonth);
10435         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10436         this.updateMPYear(this.mpSelYear);
10437
10438         this.monthPicker.slideIn('t', {duration:.2});
10439     },
10440
10441     updateMPYear : function(y){
10442         this.mpyear = y;
10443         var ys = this.mpYears.elements;
10444         for(var i = 1; i <= 10; i++){
10445             var td = ys[i-1], y2;
10446             if((i%2) == 0){
10447                 y2 = y + Math.round(i * .5);
10448                 td.firstChild.innerHTML = y2;
10449                 td.xyear = y2;
10450             }else{
10451                 y2 = y - (5-Math.round(i * .5));
10452                 td.firstChild.innerHTML = y2;
10453                 td.xyear = y2;
10454             }
10455             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10456         }
10457     },
10458
10459     updateMPMonth : function(sm){
10460         this.mpMonths.each(function(m, a, i){
10461             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10462         });
10463     },
10464
10465     selectMPMonth: function(m){
10466         
10467     },
10468
10469     onMonthClick : function(e, t){
10470         e.stopEvent();
10471         var el = new Roo.Element(t), pn;
10472         if(el.is('button.x-date-mp-cancel')){
10473             this.hideMonthPicker();
10474         }
10475         else if(el.is('button.x-date-mp-ok')){
10476             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10477             this.hideMonthPicker();
10478         }
10479         else if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.mpMonths.removeClass('x-date-mp-sel');
10481             pn.addClass('x-date-mp-sel');
10482             this.mpSelMonth = pn.dom.xmonth;
10483         }
10484         else if(pn = el.up('td.x-date-mp-year', 2)){
10485             this.mpYears.removeClass('x-date-mp-sel');
10486             pn.addClass('x-date-mp-sel');
10487             this.mpSelYear = pn.dom.xyear;
10488         }
10489         else if(el.is('a.x-date-mp-prev')){
10490             this.updateMPYear(this.mpyear-10);
10491         }
10492         else if(el.is('a.x-date-mp-next')){
10493             this.updateMPYear(this.mpyear+10);
10494         }
10495     },
10496
10497     onMonthDblClick : function(e, t){
10498         e.stopEvent();
10499         var el = new Roo.Element(t), pn;
10500         if(pn = el.up('td.x-date-mp-month', 2)){
10501             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10502             this.hideMonthPicker();
10503         }
10504         else if(pn = el.up('td.x-date-mp-year', 2)){
10505             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10506             this.hideMonthPicker();
10507         }
10508     },
10509
10510     hideMonthPicker : function(disableAnim){
10511         if(this.monthPicker){
10512             if(disableAnim === true){
10513                 this.monthPicker.hide();
10514             }else{
10515                 this.monthPicker.slideOut('t', {duration:.2});
10516             }
10517         }
10518     },
10519
10520     // private
10521     showPrevMonth : function(e){
10522         this.update(this.activeDate.add("mo", -1));
10523     },
10524
10525     // private
10526     showNextMonth : function(e){
10527         this.update(this.activeDate.add("mo", 1));
10528     },
10529
10530     // private
10531     showPrevYear : function(){
10532         this.update(this.activeDate.add("y", -1));
10533     },
10534
10535     // private
10536     showNextYear : function(){
10537         this.update(this.activeDate.add("y", 1));
10538     },
10539
10540     // private
10541     handleMouseWheel : function(e){
10542         var delta = e.getWheelDelta();
10543         if(delta > 0){
10544             this.showPrevMonth();
10545             e.stopEvent();
10546         } else if(delta < 0){
10547             this.showNextMonth();
10548             e.stopEvent();
10549         }
10550     },
10551
10552     // private
10553     handleDateClick : function(e, t){
10554         e.stopEvent();
10555         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10556             this.setValue(new Date(t.dateValue));
10557             this.fireEvent("select", this, this.value);
10558         }
10559     },
10560
10561     // private
10562     selectToday : function(){
10563         this.setValue(new Date().clearTime());
10564         this.fireEvent("select", this, this.value);
10565     },
10566
10567     // private
10568     update : function(date){
10569         var vd = this.activeDate;
10570         this.activeDate = date;
10571         if(vd && this.el){
10572             var t = date.getTime();
10573             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10574                 this.cells.removeClass("x-date-selected");
10575                 this.cells.each(function(c){
10576                    if(c.dom.firstChild.dateValue == t){
10577                        c.addClass("x-date-selected");
10578                        setTimeout(function(){
10579                             try{c.dom.firstChild.focus();}catch(e){}
10580                        }, 50);
10581                        return false;
10582                    }
10583                 });
10584                 return;
10585             }
10586         }
10587         var days = date.getDaysInMonth();
10588         var firstOfMonth = date.getFirstDateOfMonth();
10589         var startingPos = firstOfMonth.getDay()-this.startDay;
10590
10591         if(startingPos <= this.startDay){
10592             startingPos += 7;
10593         }
10594
10595         var pm = date.add("mo", -1);
10596         var prevStart = pm.getDaysInMonth()-startingPos;
10597
10598         var cells = this.cells.elements;
10599         var textEls = this.textNodes;
10600         days += startingPos;
10601
10602         // convert everything to numbers so it's fast
10603         var day = 86400000;
10604         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10605         var today = new Date().clearTime().getTime();
10606         var sel = date.clearTime().getTime();
10607         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10608         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10609         var ddMatch = this.disabledDatesRE;
10610         var ddText = this.disabledDatesText;
10611         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10612         var ddaysText = this.disabledDaysText;
10613         var format = this.format;
10614
10615         var setCellClass = function(cal, cell){
10616             cell.title = "";
10617             var t = d.getTime();
10618             cell.firstChild.dateValue = t;
10619             if(t == today){
10620                 cell.className += " x-date-today";
10621                 cell.title = cal.todayText;
10622             }
10623             if(t == sel){
10624                 cell.className += " x-date-selected";
10625                 setTimeout(function(){
10626                     try{cell.firstChild.focus();}catch(e){}
10627                 }, 50);
10628             }
10629             // disabling
10630             if(t < min) {
10631                 cell.className = " x-date-disabled";
10632                 cell.title = cal.minText;
10633                 return;
10634             }
10635             if(t > max) {
10636                 cell.className = " x-date-disabled";
10637                 cell.title = cal.maxText;
10638                 return;
10639             }
10640             if(ddays){
10641                 if(ddays.indexOf(d.getDay()) != -1){
10642                     cell.title = ddaysText;
10643                     cell.className = " x-date-disabled";
10644                 }
10645             }
10646             if(ddMatch && format){
10647                 var fvalue = d.dateFormat(format);
10648                 if(ddMatch.test(fvalue)){
10649                     cell.title = ddText.replace("%0", fvalue);
10650                     cell.className = " x-date-disabled";
10651                 }
10652             }
10653         };
10654
10655         var i = 0;
10656         for(; i < startingPos; i++) {
10657             textEls[i].innerHTML = (++prevStart);
10658             d.setDate(d.getDate()+1);
10659             cells[i].className = "x-date-prevday";
10660             setCellClass(this, cells[i]);
10661         }
10662         for(; i < days; i++){
10663             intDay = i - startingPos + 1;
10664             textEls[i].innerHTML = (intDay);
10665             d.setDate(d.getDate()+1);
10666             cells[i].className = "x-date-active";
10667             setCellClass(this, cells[i]);
10668         }
10669         var extraDays = 0;
10670         for(; i < 42; i++) {
10671              textEls[i].innerHTML = (++extraDays);
10672              d.setDate(d.getDate()+1);
10673              cells[i].className = "x-date-nextday";
10674              setCellClass(this, cells[i]);
10675         }
10676
10677         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10678
10679         if(!this.internalRender){
10680             var main = this.el.dom.firstChild;
10681             var w = main.offsetWidth;
10682             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10683             Roo.fly(main).setWidth(w);
10684             this.internalRender = true;
10685             // opera does not respect the auto grow header center column
10686             // then, after it gets a width opera refuses to recalculate
10687             // without a second pass
10688             if(Roo.isOpera && !this.secondPass){
10689                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10690                 this.secondPass = true;
10691                 this.update.defer(10, this, [date]);
10692             }
10693         }
10694     }
10695 });        /*
10696  * Based on:
10697  * Ext JS Library 1.1.1
10698  * Copyright(c) 2006-2007, Ext JS, LLC.
10699  *
10700  * Originally Released Under LGPL - original licence link has changed is not relivant.
10701  *
10702  * Fork - LGPL
10703  * <script type="text/javascript">
10704  */
10705 /**
10706  * @class Roo.TabPanel
10707  * @extends Roo.util.Observable
10708  * A lightweight tab container.
10709  * <br><br>
10710  * Usage:
10711  * <pre><code>
10712 // basic tabs 1, built from existing content
10713 var tabs = new Roo.TabPanel("tabs1");
10714 tabs.addTab("script", "View Script");
10715 tabs.addTab("markup", "View Markup");
10716 tabs.activate("script");
10717
10718 // more advanced tabs, built from javascript
10719 var jtabs = new Roo.TabPanel("jtabs");
10720 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10721
10722 // set up the UpdateManager
10723 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10724 var updater = tab2.getUpdateManager();
10725 updater.setDefaultUrl("ajax1.htm");
10726 tab2.on('activate', updater.refresh, updater, true);
10727
10728 // Use setUrl for Ajax loading
10729 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10730 tab3.setUrl("ajax2.htm", null, true);
10731
10732 // Disabled tab
10733 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10734 tab4.disable();
10735
10736 jtabs.activate("jtabs-1");
10737  * </code></pre>
10738  * @constructor
10739  * Create a new TabPanel.
10740  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10741  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10742  */
10743 Roo.TabPanel = function(container, config){
10744     /**
10745     * The container element for this TabPanel.
10746     * @type Roo.Element
10747     */
10748     this.el = Roo.get(container, true);
10749     if(config){
10750         if(typeof config == "boolean"){
10751             this.tabPosition = config ? "bottom" : "top";
10752         }else{
10753             Roo.apply(this, config);
10754         }
10755     }
10756     if(this.tabPosition == "bottom"){
10757         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10758         this.el.addClass("x-tabs-bottom");
10759     }
10760     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10761     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10762     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10763     if(Roo.isIE){
10764         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10765     }
10766     if(this.tabPosition != "bottom"){
10767         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10768          * @type Roo.Element
10769          */
10770         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10771         this.el.addClass("x-tabs-top");
10772     }
10773     this.items = [];
10774
10775     this.bodyEl.setStyle("position", "relative");
10776
10777     this.active = null;
10778     this.activateDelegate = this.activate.createDelegate(this);
10779
10780     this.addEvents({
10781         /**
10782          * @event tabchange
10783          * Fires when the active tab changes
10784          * @param {Roo.TabPanel} this
10785          * @param {Roo.TabPanelItem} activePanel The new active tab
10786          */
10787         "tabchange": true,
10788         /**
10789          * @event beforetabchange
10790          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10791          * @param {Roo.TabPanel} this
10792          * @param {Object} e Set cancel to true on this object to cancel the tab change
10793          * @param {Roo.TabPanelItem} tab The tab being changed to
10794          */
10795         "beforetabchange" : true
10796     });
10797
10798     Roo.EventManager.onWindowResize(this.onResize, this);
10799     this.cpad = this.el.getPadding("lr");
10800     this.hiddenCount = 0;
10801
10802
10803     // toolbar on the tabbar support...
10804     if (this.toolbar) {
10805         var tcfg = this.toolbar;
10806         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10807         this.toolbar = new Roo.Toolbar(tcfg);
10808         if (Roo.isSafari) {
10809             var tbl = tcfg.container.child('table', true);
10810             tbl.setAttribute('width', '100%');
10811         }
10812         
10813     }
10814    
10815
10816
10817     Roo.TabPanel.superclass.constructor.call(this);
10818 };
10819
10820 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10821     /*
10822      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10823      */
10824     tabPosition : "top",
10825     /*
10826      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10827      */
10828     currentTabWidth : 0,
10829     /*
10830      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10831      */
10832     minTabWidth : 40,
10833     /*
10834      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10835      */
10836     maxTabWidth : 250,
10837     /*
10838      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10839      */
10840     preferredTabWidth : 175,
10841     /*
10842      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10843      */
10844     resizeTabs : false,
10845     /*
10846      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10847      */
10848     monitorResize : true,
10849     /*
10850      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10851      */
10852     toolbar : false,
10853
10854     /**
10855      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10856      * @param {String} id The id of the div to use <b>or create</b>
10857      * @param {String} text The text for the tab
10858      * @param {String} content (optional) Content to put in the TabPanelItem body
10859      * @param {Boolean} closable (optional) True to create a close icon on the tab
10860      * @return {Roo.TabPanelItem} The created TabPanelItem
10861      */
10862     addTab : function(id, text, content, closable){
10863         var item = new Roo.TabPanelItem(this, id, text, closable);
10864         this.addTabItem(item);
10865         if(content){
10866             item.setContent(content);
10867         }
10868         return item;
10869     },
10870
10871     /**
10872      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10873      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10874      * @return {Roo.TabPanelItem}
10875      */
10876     getTab : function(id){
10877         return this.items[id];
10878     },
10879
10880     /**
10881      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10882      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10883      */
10884     hideTab : function(id){
10885         var t = this.items[id];
10886         if(!t.isHidden()){
10887            t.setHidden(true);
10888            this.hiddenCount++;
10889            this.autoSizeTabs();
10890         }
10891     },
10892
10893     /**
10894      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10895      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10896      */
10897     unhideTab : function(id){
10898         var t = this.items[id];
10899         if(t.isHidden()){
10900            t.setHidden(false);
10901            this.hiddenCount--;
10902            this.autoSizeTabs();
10903         }
10904     },
10905
10906     /**
10907      * Adds an existing {@link Roo.TabPanelItem}.
10908      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10909      */
10910     addTabItem : function(item){
10911         this.items[item.id] = item;
10912         this.items.push(item);
10913         if(this.resizeTabs){
10914            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10915            this.autoSizeTabs();
10916         }else{
10917             item.autoSize();
10918         }
10919     },
10920
10921     /**
10922      * Removes a {@link Roo.TabPanelItem}.
10923      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10924      */
10925     removeTab : function(id){
10926         var items = this.items;
10927         var tab = items[id];
10928         if(!tab) { return; }
10929         var index = items.indexOf(tab);
10930         if(this.active == tab && items.length > 1){
10931             var newTab = this.getNextAvailable(index);
10932             if(newTab) {
10933                 newTab.activate();
10934             }
10935         }
10936         this.stripEl.dom.removeChild(tab.pnode.dom);
10937         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10938             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10939         }
10940         items.splice(index, 1);
10941         delete this.items[tab.id];
10942         tab.fireEvent("close", tab);
10943         tab.purgeListeners();
10944         this.autoSizeTabs();
10945     },
10946
10947     getNextAvailable : function(start){
10948         var items = this.items;
10949         var index = start;
10950         // look for a next tab that will slide over to
10951         // replace the one being removed
10952         while(index < items.length){
10953             var item = items[++index];
10954             if(item && !item.isHidden()){
10955                 return item;
10956             }
10957         }
10958         // if one isn't found select the previous tab (on the left)
10959         index = start;
10960         while(index >= 0){
10961             var item = items[--index];
10962             if(item && !item.isHidden()){
10963                 return item;
10964             }
10965         }
10966         return null;
10967     },
10968
10969     /**
10970      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10971      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10972      */
10973     disableTab : function(id){
10974         var tab = this.items[id];
10975         if(tab && this.active != tab){
10976             tab.disable();
10977         }
10978     },
10979
10980     /**
10981      * Enables a {@link Roo.TabPanelItem} that is disabled.
10982      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10983      */
10984     enableTab : function(id){
10985         var tab = this.items[id];
10986         tab.enable();
10987     },
10988
10989     /**
10990      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10991      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10992      * @return {Roo.TabPanelItem} The TabPanelItem.
10993      */
10994     activate : function(id){
10995         var tab = this.items[id];
10996         if(!tab){
10997             return null;
10998         }
10999         if(tab == this.active || tab.disabled){
11000             return tab;
11001         }
11002         var e = {};
11003         this.fireEvent("beforetabchange", this, e, tab);
11004         if(e.cancel !== true && !tab.disabled){
11005             if(this.active){
11006                 this.active.hide();
11007             }
11008             this.active = this.items[id];
11009             this.active.show();
11010             this.fireEvent("tabchange", this, this.active);
11011         }
11012         return tab;
11013     },
11014
11015     /**
11016      * Gets the active {@link Roo.TabPanelItem}.
11017      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11018      */
11019     getActiveTab : function(){
11020         return this.active;
11021     },
11022
11023     /**
11024      * Updates the tab body element to fit the height of the container element
11025      * for overflow scrolling
11026      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11027      */
11028     syncHeight : function(targetHeight){
11029         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11030         var bm = this.bodyEl.getMargins();
11031         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11032         this.bodyEl.setHeight(newHeight);
11033         return newHeight;
11034     },
11035
11036     onResize : function(){
11037         if(this.monitorResize){
11038             this.autoSizeTabs();
11039         }
11040     },
11041
11042     /**
11043      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11044      */
11045     beginUpdate : function(){
11046         this.updating = true;
11047     },
11048
11049     /**
11050      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11051      */
11052     endUpdate : function(){
11053         this.updating = false;
11054         this.autoSizeTabs();
11055     },
11056
11057     /**
11058      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11059      */
11060     autoSizeTabs : function(){
11061         var count = this.items.length;
11062         var vcount = count - this.hiddenCount;
11063         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11064         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11065         var availWidth = Math.floor(w / vcount);
11066         var b = this.stripBody;
11067         if(b.getWidth() > w){
11068             var tabs = this.items;
11069             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11070             if(availWidth < this.minTabWidth){
11071                 /*if(!this.sleft){    // incomplete scrolling code
11072                     this.createScrollButtons();
11073                 }
11074                 this.showScroll();
11075                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11076             }
11077         }else{
11078             if(this.currentTabWidth < this.preferredTabWidth){
11079                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11080             }
11081         }
11082     },
11083
11084     /**
11085      * Returns the number of tabs in this TabPanel.
11086      * @return {Number}
11087      */
11088      getCount : function(){
11089          return this.items.length;
11090      },
11091
11092     /**
11093      * Resizes all the tabs to the passed width
11094      * @param {Number} The new width
11095      */
11096     setTabWidth : function(width){
11097         this.currentTabWidth = width;
11098         for(var i = 0, len = this.items.length; i < len; i++) {
11099                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11100         }
11101     },
11102
11103     /**
11104      * Destroys this TabPanel
11105      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11106      */
11107     destroy : function(removeEl){
11108         Roo.EventManager.removeResizeListener(this.onResize, this);
11109         for(var i = 0, len = this.items.length; i < len; i++){
11110             this.items[i].purgeListeners();
11111         }
11112         if(removeEl === true){
11113             this.el.update("");
11114             this.el.remove();
11115         }
11116     }
11117 });
11118
11119 /**
11120  * @class Roo.TabPanelItem
11121  * @extends Roo.util.Observable
11122  * Represents an individual item (tab plus body) in a TabPanel.
11123  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11124  * @param {String} id The id of this TabPanelItem
11125  * @param {String} text The text for the tab of this TabPanelItem
11126  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11127  */
11128 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11129     /**
11130      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11131      * @type Roo.TabPanel
11132      */
11133     this.tabPanel = tabPanel;
11134     /**
11135      * The id for this TabPanelItem
11136      * @type String
11137      */
11138     this.id = id;
11139     /** @private */
11140     this.disabled = false;
11141     /** @private */
11142     this.text = text;
11143     /** @private */
11144     this.loaded = false;
11145     this.closable = closable;
11146
11147     /**
11148      * The body element for this TabPanelItem.
11149      * @type Roo.Element
11150      */
11151     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11152     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11153     this.bodyEl.setStyle("display", "block");
11154     this.bodyEl.setStyle("zoom", "1");
11155     this.hideAction();
11156
11157     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11158     /** @private */
11159     this.el = Roo.get(els.el, true);
11160     this.inner = Roo.get(els.inner, true);
11161     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11162     this.pnode = Roo.get(els.el.parentNode, true);
11163     this.el.on("mousedown", this.onTabMouseDown, this);
11164     this.el.on("click", this.onTabClick, this);
11165     /** @private */
11166     if(closable){
11167         var c = Roo.get(els.close, true);
11168         c.dom.title = this.closeText;
11169         c.addClassOnOver("close-over");
11170         c.on("click", this.closeClick, this);
11171      }
11172
11173     this.addEvents({
11174          /**
11175          * @event activate
11176          * Fires when this tab becomes the active tab.
11177          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11178          * @param {Roo.TabPanelItem} this
11179          */
11180         "activate": true,
11181         /**
11182          * @event beforeclose
11183          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11184          * @param {Roo.TabPanelItem} this
11185          * @param {Object} e Set cancel to true on this object to cancel the close.
11186          */
11187         "beforeclose": true,
11188         /**
11189          * @event close
11190          * Fires when this tab is closed.
11191          * @param {Roo.TabPanelItem} this
11192          */
11193          "close": true,
11194         /**
11195          * @event deactivate
11196          * Fires when this tab is no longer the active tab.
11197          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11198          * @param {Roo.TabPanelItem} this
11199          */
11200          "deactivate" : true
11201     });
11202     this.hidden = false;
11203
11204     Roo.TabPanelItem.superclass.constructor.call(this);
11205 };
11206
11207 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11208     purgeListeners : function(){
11209        Roo.util.Observable.prototype.purgeListeners.call(this);
11210        this.el.removeAllListeners();
11211     },
11212     /**
11213      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11214      */
11215     show : function(){
11216         this.pnode.addClass("on");
11217         this.showAction();
11218         if(Roo.isOpera){
11219             this.tabPanel.stripWrap.repaint();
11220         }
11221         this.fireEvent("activate", this.tabPanel, this);
11222     },
11223
11224     /**
11225      * Returns true if this tab is the active tab.
11226      * @return {Boolean}
11227      */
11228     isActive : function(){
11229         return this.tabPanel.getActiveTab() == this;
11230     },
11231
11232     /**
11233      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11234      */
11235     hide : function(){
11236         this.pnode.removeClass("on");
11237         this.hideAction();
11238         this.fireEvent("deactivate", this.tabPanel, this);
11239     },
11240
11241     hideAction : function(){
11242         this.bodyEl.hide();
11243         this.bodyEl.setStyle("position", "absolute");
11244         this.bodyEl.setLeft("-20000px");
11245         this.bodyEl.setTop("-20000px");
11246     },
11247
11248     showAction : function(){
11249         this.bodyEl.setStyle("position", "relative");
11250         this.bodyEl.setTop("");
11251         this.bodyEl.setLeft("");
11252         this.bodyEl.show();
11253     },
11254
11255     /**
11256      * Set the tooltip for the tab.
11257      * @param {String} tooltip The tab's tooltip
11258      */
11259     setTooltip : function(text){
11260         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11261             this.textEl.dom.qtip = text;
11262             this.textEl.dom.removeAttribute('title');
11263         }else{
11264             this.textEl.dom.title = text;
11265         }
11266     },
11267
11268     onTabClick : function(e){
11269         e.preventDefault();
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     onTabMouseDown : function(e){
11274         e.preventDefault();
11275         this.tabPanel.activate(this.id);
11276     },
11277
11278     getWidth : function(){
11279         return this.inner.getWidth();
11280     },
11281
11282     setWidth : function(width){
11283         var iwidth = width - this.pnode.getPadding("lr");
11284         this.inner.setWidth(iwidth);
11285         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11286         this.pnode.setWidth(width);
11287     },
11288
11289     /**
11290      * Show or hide the tab
11291      * @param {Boolean} hidden True to hide or false to show.
11292      */
11293     setHidden : function(hidden){
11294         this.hidden = hidden;
11295         this.pnode.setStyle("display", hidden ? "none" : "");
11296     },
11297
11298     /**
11299      * Returns true if this tab is "hidden"
11300      * @return {Boolean}
11301      */
11302     isHidden : function(){
11303         return this.hidden;
11304     },
11305
11306     /**
11307      * Returns the text for this tab
11308      * @return {String}
11309      */
11310     getText : function(){
11311         return this.text;
11312     },
11313
11314     autoSize : function(){
11315         //this.el.beginMeasure();
11316         this.textEl.setWidth(1);
11317         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11318         //this.el.endMeasure();
11319     },
11320
11321     /**
11322      * Sets the text for the tab (Note: this also sets the tooltip text)
11323      * @param {String} text The tab's text and tooltip
11324      */
11325     setText : function(text){
11326         this.text = text;
11327         this.textEl.update(text);
11328         this.setTooltip(text);
11329         if(!this.tabPanel.resizeTabs){
11330             this.autoSize();
11331         }
11332     },
11333     /**
11334      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11335      */
11336     activate : function(){
11337         this.tabPanel.activate(this.id);
11338     },
11339
11340     /**
11341      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11342      */
11343     disable : function(){
11344         if(this.tabPanel.active != this){
11345             this.disabled = true;
11346             this.pnode.addClass("disabled");
11347         }
11348     },
11349
11350     /**
11351      * Enables this TabPanelItem if it was previously disabled.
11352      */
11353     enable : function(){
11354         this.disabled = false;
11355         this.pnode.removeClass("disabled");
11356     },
11357
11358     /**
11359      * Sets the content for this TabPanelItem.
11360      * @param {String} content The content
11361      * @param {Boolean} loadScripts true to look for and load scripts
11362      */
11363     setContent : function(content, loadScripts){
11364         this.bodyEl.update(content, loadScripts);
11365     },
11366
11367     /**
11368      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11369      * @return {Roo.UpdateManager} The UpdateManager
11370      */
11371     getUpdateManager : function(){
11372         return this.bodyEl.getUpdateManager();
11373     },
11374
11375     /**
11376      * Set a URL to be used to load the content for this TabPanelItem.
11377      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11378      * @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)
11379      * @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)
11380      * @return {Roo.UpdateManager} The UpdateManager
11381      */
11382     setUrl : function(url, params, loadOnce){
11383         if(this.refreshDelegate){
11384             this.un('activate', this.refreshDelegate);
11385         }
11386         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11387         this.on("activate", this.refreshDelegate);
11388         return this.bodyEl.getUpdateManager();
11389     },
11390
11391     /** @private */
11392     _handleRefresh : function(url, params, loadOnce){
11393         if(!loadOnce || !this.loaded){
11394             var updater = this.bodyEl.getUpdateManager();
11395             updater.update(url, params, this._setLoaded.createDelegate(this));
11396         }
11397     },
11398
11399     /**
11400      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11401      *   Will fail silently if the setUrl method has not been called.
11402      *   This does not activate the panel, just updates its content.
11403      */
11404     refresh : function(){
11405         if(this.refreshDelegate){
11406            this.loaded = false;
11407            this.refreshDelegate();
11408         }
11409     },
11410
11411     /** @private */
11412     _setLoaded : function(){
11413         this.loaded = true;
11414     },
11415
11416     /** @private */
11417     closeClick : function(e){
11418         var o = {};
11419         e.stopEvent();
11420         this.fireEvent("beforeclose", this, o);
11421         if(o.cancel !== true){
11422             this.tabPanel.removeTab(this.id);
11423         }
11424     },
11425     /**
11426      * The text displayed in the tooltip for the close icon.
11427      * @type String
11428      */
11429     closeText : "Close this tab"
11430 });
11431
11432 /** @private */
11433 Roo.TabPanel.prototype.createStrip = function(container){
11434     var strip = document.createElement("div");
11435     strip.className = "x-tabs-wrap";
11436     container.appendChild(strip);
11437     return strip;
11438 };
11439 /** @private */
11440 Roo.TabPanel.prototype.createStripList = function(strip){
11441     // div wrapper for retard IE
11442     // returns the "tr" element.
11443     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11444         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11445         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11446     return strip.firstChild.firstChild.firstChild.firstChild;
11447 };
11448 /** @private */
11449 Roo.TabPanel.prototype.createBody = function(container){
11450     var body = document.createElement("div");
11451     Roo.id(body, "tab-body");
11452     Roo.fly(body).addClass("x-tabs-body");
11453     container.appendChild(body);
11454     return body;
11455 };
11456 /** @private */
11457 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11458     var body = Roo.getDom(id);
11459     if(!body){
11460         body = document.createElement("div");
11461         body.id = id;
11462     }
11463     Roo.fly(body).addClass("x-tabs-item-body");
11464     bodyEl.insertBefore(body, bodyEl.firstChild);
11465     return body;
11466 };
11467 /** @private */
11468 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11469     var td = document.createElement("td");
11470     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11471     //stripEl.appendChild(td);
11472     if(closable){
11473         td.className = "x-tabs-closable";
11474         if(!this.closeTpl){
11475             this.closeTpl = new Roo.Template(
11476                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11477                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11478                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11479             );
11480         }
11481         var el = this.closeTpl.overwrite(td, {"text": text});
11482         var close = el.getElementsByTagName("div")[0];
11483         var inner = el.getElementsByTagName("em")[0];
11484         return {"el": el, "close": close, "inner": inner};
11485     } else {
11486         if(!this.tabTpl){
11487             this.tabTpl = new Roo.Template(
11488                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11489                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11490             );
11491         }
11492         var el = this.tabTpl.overwrite(td, {"text": text});
11493         var inner = el.getElementsByTagName("em")[0];
11494         return {"el": el, "inner": inner};
11495     }
11496 };/*
11497  * Based on:
11498  * Ext JS Library 1.1.1
11499  * Copyright(c) 2006-2007, Ext JS, LLC.
11500  *
11501  * Originally Released Under LGPL - original licence link has changed is not relivant.
11502  *
11503  * Fork - LGPL
11504  * <script type="text/javascript">
11505  */
11506
11507 /**
11508  * @class Roo.Button
11509  * @extends Roo.util.Observable
11510  * Simple Button class
11511  * @cfg {String} text The button text
11512  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11513  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11514  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11515  * @cfg {Object} scope The scope of the handler
11516  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11517  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11518  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11519  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11520  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11521  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11522    applies if enableToggle = true)
11523  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11524  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11525   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11526  * @constructor
11527  * Create a new button
11528  * @param {Object} config The config object
11529  */
11530 Roo.Button = function(renderTo, config)
11531 {
11532     if (!config) {
11533         config = renderTo;
11534         renderTo = config.renderTo || false;
11535     }
11536     
11537     Roo.apply(this, config);
11538     this.addEvents({
11539         /**
11540              * @event click
11541              * Fires when this button is clicked
11542              * @param {Button} this
11543              * @param {EventObject} e The click event
11544              */
11545             "click" : true,
11546         /**
11547              * @event toggle
11548              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11549              * @param {Button} this
11550              * @param {Boolean} pressed
11551              */
11552             "toggle" : true,
11553         /**
11554              * @event mouseover
11555              * Fires when the mouse hovers over the button
11556              * @param {Button} this
11557              * @param {Event} e The event object
11558              */
11559         'mouseover' : true,
11560         /**
11561              * @event mouseout
11562              * Fires when the mouse exits the button
11563              * @param {Button} this
11564              * @param {Event} e The event object
11565              */
11566         'mouseout': true,
11567          /**
11568              * @event render
11569              * Fires when the button is rendered
11570              * @param {Button} this
11571              */
11572         'render': true
11573     });
11574     if(this.menu){
11575         this.menu = Roo.menu.MenuMgr.get(this.menu);
11576     }
11577     // register listeners first!!  - so render can be captured..
11578     Roo.util.Observable.call(this);
11579     if(renderTo){
11580         this.render(renderTo);
11581     }
11582     
11583   
11584 };
11585
11586 Roo.extend(Roo.Button, Roo.util.Observable, {
11587     /**
11588      * 
11589      */
11590     
11591     /**
11592      * Read-only. True if this button is hidden
11593      * @type Boolean
11594      */
11595     hidden : false,
11596     /**
11597      * Read-only. True if this button is disabled
11598      * @type Boolean
11599      */
11600     disabled : false,
11601     /**
11602      * Read-only. True if this button is pressed (only if enableToggle = true)
11603      * @type Boolean
11604      */
11605     pressed : false,
11606
11607     /**
11608      * @cfg {Number} tabIndex 
11609      * The DOM tabIndex for this button (defaults to undefined)
11610      */
11611     tabIndex : undefined,
11612
11613     /**
11614      * @cfg {Boolean} enableToggle
11615      * True to enable pressed/not pressed toggling (defaults to false)
11616      */
11617     enableToggle: false,
11618     /**
11619      * @cfg {Mixed} menu
11620      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11621      */
11622     menu : undefined,
11623     /**
11624      * @cfg {String} menuAlign
11625      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11626      */
11627     menuAlign : "tl-bl?",
11628
11629     /**
11630      * @cfg {String} iconCls
11631      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11632      */
11633     iconCls : undefined,
11634     /**
11635      * @cfg {String} type
11636      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11637      */
11638     type : 'button',
11639
11640     // private
11641     menuClassTarget: 'tr',
11642
11643     /**
11644      * @cfg {String} clickEvent
11645      * The type of event to map to the button's event handler (defaults to 'click')
11646      */
11647     clickEvent : 'click',
11648
11649     /**
11650      * @cfg {Boolean} handleMouseEvents
11651      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11652      */
11653     handleMouseEvents : true,
11654
11655     /**
11656      * @cfg {String} tooltipType
11657      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11658      */
11659     tooltipType : 'qtip',
11660
11661     /**
11662      * @cfg {String} cls
11663      * A CSS class to apply to the button's main element.
11664      */
11665     
11666     /**
11667      * @cfg {Roo.Template} template (Optional)
11668      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11669      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11670      * require code modifications if required elements (e.g. a button) aren't present.
11671      */
11672
11673     // private
11674     render : function(renderTo){
11675         var btn;
11676         if(this.hideParent){
11677             this.parentEl = Roo.get(renderTo);
11678         }
11679         if(!this.dhconfig){
11680             if(!this.template){
11681                 if(!Roo.Button.buttonTemplate){
11682                     // hideous table template
11683                     Roo.Button.buttonTemplate = new Roo.Template(
11684                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11685                         '<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>',
11686                         "</tr></tbody></table>");
11687                 }
11688                 this.template = Roo.Button.buttonTemplate;
11689             }
11690             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11691             var btnEl = btn.child("button:first");
11692             btnEl.on('focus', this.onFocus, this);
11693             btnEl.on('blur', this.onBlur, this);
11694             if(this.cls){
11695                 btn.addClass(this.cls);
11696             }
11697             if(this.icon){
11698                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11699             }
11700             if(this.iconCls){
11701                 btnEl.addClass(this.iconCls);
11702                 if(!this.cls){
11703                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11704                 }
11705             }
11706             if(this.tabIndex !== undefined){
11707                 btnEl.dom.tabIndex = this.tabIndex;
11708             }
11709             if(this.tooltip){
11710                 if(typeof this.tooltip == 'object'){
11711                     Roo.QuickTips.tips(Roo.apply({
11712                           target: btnEl.id
11713                     }, this.tooltip));
11714                 } else {
11715                     btnEl.dom[this.tooltipType] = this.tooltip;
11716                 }
11717             }
11718         }else{
11719             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11720         }
11721         this.el = btn;
11722         if(this.id){
11723             this.el.dom.id = this.el.id = this.id;
11724         }
11725         if(this.menu){
11726             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11727             this.menu.on("show", this.onMenuShow, this);
11728             this.menu.on("hide", this.onMenuHide, this);
11729         }
11730         btn.addClass("x-btn");
11731         if(Roo.isIE && !Roo.isIE7){
11732             this.autoWidth.defer(1, this);
11733         }else{
11734             this.autoWidth();
11735         }
11736         if(this.handleMouseEvents){
11737             btn.on("mouseover", this.onMouseOver, this);
11738             btn.on("mouseout", this.onMouseOut, this);
11739             btn.on("mousedown", this.onMouseDown, this);
11740         }
11741         btn.on(this.clickEvent, this.onClick, this);
11742         //btn.on("mouseup", this.onMouseUp, this);
11743         if(this.hidden){
11744             this.hide();
11745         }
11746         if(this.disabled){
11747             this.disable();
11748         }
11749         Roo.ButtonToggleMgr.register(this);
11750         if(this.pressed){
11751             this.el.addClass("x-btn-pressed");
11752         }
11753         if(this.repeat){
11754             var repeater = new Roo.util.ClickRepeater(btn,
11755                 typeof this.repeat == "object" ? this.repeat : {}
11756             );
11757             repeater.on("click", this.onClick,  this);
11758         }
11759         
11760         this.fireEvent('render', this);
11761         
11762     },
11763     /**
11764      * Returns the button's underlying element
11765      * @return {Roo.Element} The element
11766      */
11767     getEl : function(){
11768         return this.el;  
11769     },
11770     
11771     /**
11772      * Destroys this Button and removes any listeners.
11773      */
11774     destroy : function(){
11775         Roo.ButtonToggleMgr.unregister(this);
11776         this.el.removeAllListeners();
11777         this.purgeListeners();
11778         this.el.remove();
11779     },
11780
11781     // private
11782     autoWidth : function(){
11783         if(this.el){
11784             this.el.setWidth("auto");
11785             if(Roo.isIE7 && Roo.isStrict){
11786                 var ib = this.el.child('button');
11787                 if(ib && ib.getWidth() > 20){
11788                     ib.clip();
11789                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11790                 }
11791             }
11792             if(this.minWidth){
11793                 if(this.hidden){
11794                     this.el.beginMeasure();
11795                 }
11796                 if(this.el.getWidth() < this.minWidth){
11797                     this.el.setWidth(this.minWidth);
11798                 }
11799                 if(this.hidden){
11800                     this.el.endMeasure();
11801                 }
11802             }
11803         }
11804     },
11805
11806     /**
11807      * Assigns this button's click handler
11808      * @param {Function} handler The function to call when the button is clicked
11809      * @param {Object} scope (optional) Scope for the function passed in
11810      */
11811     setHandler : function(handler, scope){
11812         this.handler = handler;
11813         this.scope = scope;  
11814     },
11815     
11816     /**
11817      * Sets this button's text
11818      * @param {String} text The button text
11819      */
11820     setText : function(text){
11821         this.text = text;
11822         if(this.el){
11823             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11824         }
11825         this.autoWidth();
11826     },
11827     
11828     /**
11829      * Gets the text for this button
11830      * @return {String} The button text
11831      */
11832     getText : function(){
11833         return this.text;  
11834     },
11835     
11836     /**
11837      * Show this button
11838      */
11839     show: function(){
11840         this.hidden = false;
11841         if(this.el){
11842             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11843         }
11844     },
11845     
11846     /**
11847      * Hide this button
11848      */
11849     hide: function(){
11850         this.hidden = true;
11851         if(this.el){
11852             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11853         }
11854     },
11855     
11856     /**
11857      * Convenience function for boolean show/hide
11858      * @param {Boolean} visible True to show, false to hide
11859      */
11860     setVisible: function(visible){
11861         if(visible) {
11862             this.show();
11863         }else{
11864             this.hide();
11865         }
11866     },
11867     
11868     /**
11869      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11870      * @param {Boolean} state (optional) Force a particular state
11871      */
11872     toggle : function(state){
11873         state = state === undefined ? !this.pressed : state;
11874         if(state != this.pressed){
11875             if(state){
11876                 this.el.addClass("x-btn-pressed");
11877                 this.pressed = true;
11878                 this.fireEvent("toggle", this, true);
11879             }else{
11880                 this.el.removeClass("x-btn-pressed");
11881                 this.pressed = false;
11882                 this.fireEvent("toggle", this, false);
11883             }
11884             if(this.toggleHandler){
11885                 this.toggleHandler.call(this.scope || this, this, state);
11886             }
11887         }
11888     },
11889     
11890     /**
11891      * Focus the button
11892      */
11893     focus : function(){
11894         this.el.child('button:first').focus();
11895     },
11896     
11897     /**
11898      * Disable this button
11899      */
11900     disable : function(){
11901         if(this.el){
11902             this.el.addClass("x-btn-disabled");
11903         }
11904         this.disabled = true;
11905     },
11906     
11907     /**
11908      * Enable this button
11909      */
11910     enable : function(){
11911         if(this.el){
11912             this.el.removeClass("x-btn-disabled");
11913         }
11914         this.disabled = false;
11915     },
11916
11917     /**
11918      * Convenience function for boolean enable/disable
11919      * @param {Boolean} enabled True to enable, false to disable
11920      */
11921     setDisabled : function(v){
11922         this[v !== true ? "enable" : "disable"]();
11923     },
11924
11925     // private
11926     onClick : function(e){
11927         if(e){
11928             e.preventDefault();
11929         }
11930         if(e.button != 0){
11931             return;
11932         }
11933         if(!this.disabled){
11934             if(this.enableToggle){
11935                 this.toggle();
11936             }
11937             if(this.menu && !this.menu.isVisible()){
11938                 this.menu.show(this.el, this.menuAlign);
11939             }
11940             this.fireEvent("click", this, e);
11941             if(this.handler){
11942                 this.el.removeClass("x-btn-over");
11943                 this.handler.call(this.scope || this, this, e);
11944             }
11945         }
11946     },
11947     // private
11948     onMouseOver : function(e){
11949         if(!this.disabled){
11950             this.el.addClass("x-btn-over");
11951             this.fireEvent('mouseover', this, e);
11952         }
11953     },
11954     // private
11955     onMouseOut : function(e){
11956         if(!e.within(this.el,  true)){
11957             this.el.removeClass("x-btn-over");
11958             this.fireEvent('mouseout', this, e);
11959         }
11960     },
11961     // private
11962     onFocus : function(e){
11963         if(!this.disabled){
11964             this.el.addClass("x-btn-focus");
11965         }
11966     },
11967     // private
11968     onBlur : function(e){
11969         this.el.removeClass("x-btn-focus");
11970     },
11971     // private
11972     onMouseDown : function(e){
11973         if(!this.disabled && e.button == 0){
11974             this.el.addClass("x-btn-click");
11975             Roo.get(document).on('mouseup', this.onMouseUp, this);
11976         }
11977     },
11978     // private
11979     onMouseUp : function(e){
11980         if(e.button == 0){
11981             this.el.removeClass("x-btn-click");
11982             Roo.get(document).un('mouseup', this.onMouseUp, this);
11983         }
11984     },
11985     // private
11986     onMenuShow : function(e){
11987         this.el.addClass("x-btn-menu-active");
11988     },
11989     // private
11990     onMenuHide : function(e){
11991         this.el.removeClass("x-btn-menu-active");
11992     }   
11993 });
11994
11995 // Private utility class used by Button
11996 Roo.ButtonToggleMgr = function(){
11997    var groups = {};
11998    
11999    function toggleGroup(btn, state){
12000        if(state){
12001            var g = groups[btn.toggleGroup];
12002            for(var i = 0, l = g.length; i < l; i++){
12003                if(g[i] != btn){
12004                    g[i].toggle(false);
12005                }
12006            }
12007        }
12008    }
12009    
12010    return {
12011        register : function(btn){
12012            if(!btn.toggleGroup){
12013                return;
12014            }
12015            var g = groups[btn.toggleGroup];
12016            if(!g){
12017                g = groups[btn.toggleGroup] = [];
12018            }
12019            g.push(btn);
12020            btn.on("toggle", toggleGroup);
12021        },
12022        
12023        unregister : function(btn){
12024            if(!btn.toggleGroup){
12025                return;
12026            }
12027            var g = groups[btn.toggleGroup];
12028            if(g){
12029                g.remove(btn);
12030                btn.un("toggle", toggleGroup);
12031            }
12032        }
12033    };
12034 }();/*
12035  * Based on:
12036  * Ext JS Library 1.1.1
12037  * Copyright(c) 2006-2007, Ext JS, LLC.
12038  *
12039  * Originally Released Under LGPL - original licence link has changed is not relivant.
12040  *
12041  * Fork - LGPL
12042  * <script type="text/javascript">
12043  */
12044  
12045 /**
12046  * @class Roo.SplitButton
12047  * @extends Roo.Button
12048  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12049  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12050  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12051  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12052  * @cfg {String} arrowTooltip The title attribute of the arrow
12053  * @constructor
12054  * Create a new menu button
12055  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12056  * @param {Object} config The config object
12057  */
12058 Roo.SplitButton = function(renderTo, config){
12059     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12060     /**
12061      * @event arrowclick
12062      * Fires when this button's arrow is clicked
12063      * @param {SplitButton} this
12064      * @param {EventObject} e The click event
12065      */
12066     this.addEvents({"arrowclick":true});
12067 };
12068
12069 Roo.extend(Roo.SplitButton, Roo.Button, {
12070     render : function(renderTo){
12071         // this is one sweet looking template!
12072         var tpl = new Roo.Template(
12073             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12074             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12075             '<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>',
12076             "</tbody></table></td><td>",
12077             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12078             '<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>',
12079             "</tbody></table></td></tr></table>"
12080         );
12081         var btn = tpl.append(renderTo, [this.text, this.type], true);
12082         var btnEl = btn.child("button");
12083         if(this.cls){
12084             btn.addClass(this.cls);
12085         }
12086         if(this.icon){
12087             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12088         }
12089         if(this.iconCls){
12090             btnEl.addClass(this.iconCls);
12091             if(!this.cls){
12092                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12093             }
12094         }
12095         this.el = btn;
12096         if(this.handleMouseEvents){
12097             btn.on("mouseover", this.onMouseOver, this);
12098             btn.on("mouseout", this.onMouseOut, this);
12099             btn.on("mousedown", this.onMouseDown, this);
12100             btn.on("mouseup", this.onMouseUp, this);
12101         }
12102         btn.on(this.clickEvent, this.onClick, this);
12103         if(this.tooltip){
12104             if(typeof this.tooltip == 'object'){
12105                 Roo.QuickTips.tips(Roo.apply({
12106                       target: btnEl.id
12107                 }, this.tooltip));
12108             } else {
12109                 btnEl.dom[this.tooltipType] = this.tooltip;
12110             }
12111         }
12112         if(this.arrowTooltip){
12113             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12114         }
12115         if(this.hidden){
12116             this.hide();
12117         }
12118         if(this.disabled){
12119             this.disable();
12120         }
12121         if(this.pressed){
12122             this.el.addClass("x-btn-pressed");
12123         }
12124         if(Roo.isIE && !Roo.isIE7){
12125             this.autoWidth.defer(1, this);
12126         }else{
12127             this.autoWidth();
12128         }
12129         if(this.menu){
12130             this.menu.on("show", this.onMenuShow, this);
12131             this.menu.on("hide", this.onMenuHide, this);
12132         }
12133         this.fireEvent('render', this);
12134     },
12135
12136     // private
12137     autoWidth : function(){
12138         if(this.el){
12139             var tbl = this.el.child("table:first");
12140             var tbl2 = this.el.child("table:last");
12141             this.el.setWidth("auto");
12142             tbl.setWidth("auto");
12143             if(Roo.isIE7 && Roo.isStrict){
12144                 var ib = this.el.child('button:first');
12145                 if(ib && ib.getWidth() > 20){
12146                     ib.clip();
12147                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12148                 }
12149             }
12150             if(this.minWidth){
12151                 if(this.hidden){
12152                     this.el.beginMeasure();
12153                 }
12154                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12155                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12156                 }
12157                 if(this.hidden){
12158                     this.el.endMeasure();
12159                 }
12160             }
12161             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12162         } 
12163     },
12164     /**
12165      * Sets this button's click handler
12166      * @param {Function} handler The function to call when the button is clicked
12167      * @param {Object} scope (optional) Scope for the function passed above
12168      */
12169     setHandler : function(handler, scope){
12170         this.handler = handler;
12171         this.scope = scope;  
12172     },
12173     
12174     /**
12175      * Sets this button's arrow click handler
12176      * @param {Function} handler The function to call when the arrow is clicked
12177      * @param {Object} scope (optional) Scope for the function passed above
12178      */
12179     setArrowHandler : function(handler, scope){
12180         this.arrowHandler = handler;
12181         this.scope = scope;  
12182     },
12183     
12184     /**
12185      * Focus the button
12186      */
12187     focus : function(){
12188         if(this.el){
12189             this.el.child("button:first").focus();
12190         }
12191     },
12192
12193     // private
12194     onClick : function(e){
12195         e.preventDefault();
12196         if(!this.disabled){
12197             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12198                 if(this.menu && !this.menu.isVisible()){
12199                     this.menu.show(this.el, this.menuAlign);
12200                 }
12201                 this.fireEvent("arrowclick", this, e);
12202                 if(this.arrowHandler){
12203                     this.arrowHandler.call(this.scope || this, this, e);
12204                 }
12205             }else{
12206                 this.fireEvent("click", this, e);
12207                 if(this.handler){
12208                     this.handler.call(this.scope || this, this, e);
12209                 }
12210             }
12211         }
12212     },
12213     // private
12214     onMouseDown : function(e){
12215         if(!this.disabled){
12216             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12217         }
12218     },
12219     // private
12220     onMouseUp : function(e){
12221         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12222     }   
12223 });
12224
12225
12226 // backwards compat
12227 Roo.MenuButton = Roo.SplitButton;/*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237
12238 /**
12239  * @class Roo.Toolbar
12240  * Basic Toolbar class.
12241  * @constructor
12242  * Creates a new Toolbar
12243  * @param {Object} config The config object
12244  */ 
12245 Roo.Toolbar = function(container, buttons, config)
12246 {
12247     /// old consturctor format still supported..
12248     if(container instanceof Array){ // omit the container for later rendering
12249         buttons = container;
12250         config = buttons;
12251         container = null;
12252     }
12253     if (typeof(container) == 'object' && container.xtype) {
12254         config = container;
12255         container = config.container;
12256         buttons = config.buttons; // not really - use items!!
12257     }
12258     var xitems = [];
12259     if (config && config.items) {
12260         xitems = config.items;
12261         delete config.items;
12262     }
12263     Roo.apply(this, config);
12264     this.buttons = buttons;
12265     
12266     if(container){
12267         this.render(container);
12268     }
12269     Roo.each(xitems, function(b) {
12270         this.add(b);
12271     }, this);
12272     
12273 };
12274
12275 Roo.Toolbar.prototype = {
12276     /**
12277      * @cfg {Roo.data.Store} items
12278      * array of button configs or elements to add
12279      */
12280     
12281     /**
12282      * @cfg {String/HTMLElement/Element} container
12283      * The id or element that will contain the toolbar
12284      */
12285     // private
12286     render : function(ct){
12287         this.el = Roo.get(ct);
12288         if(this.cls){
12289             this.el.addClass(this.cls);
12290         }
12291         // using a table allows for vertical alignment
12292         // 100% width is needed by Safari...
12293         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12294         this.tr = this.el.child("tr", true);
12295         var autoId = 0;
12296         this.items = new Roo.util.MixedCollection(false, function(o){
12297             return o.id || ("item" + (++autoId));
12298         });
12299         if(this.buttons){
12300             this.add.apply(this, this.buttons);
12301             delete this.buttons;
12302         }
12303     },
12304
12305     /**
12306      * Adds element(s) to the toolbar -- this function takes a variable number of 
12307      * arguments of mixed type and adds them to the toolbar.
12308      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12309      * <ul>
12310      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12311      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12312      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12313      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12314      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12315      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12316      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12317      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12318      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12319      * </ul>
12320      * @param {Mixed} arg2
12321      * @param {Mixed} etc.
12322      */
12323     add : function(){
12324         var a = arguments, l = a.length;
12325         for(var i = 0; i < l; i++){
12326             this._add(a[i]);
12327         }
12328     },
12329     // private..
12330     _add : function(el) {
12331         
12332         if (el.xtype) {
12333             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12334         }
12335         
12336         if (el.applyTo){ // some kind of form field
12337             return this.addField(el);
12338         } 
12339         if (el.render){ // some kind of Toolbar.Item
12340             return this.addItem(el);
12341         }
12342         if (typeof el == "string"){ // string
12343             if(el == "separator" || el == "-"){
12344                 return this.addSeparator();
12345             }
12346             if (el == " "){
12347                 return this.addSpacer();
12348             }
12349             if(el == "->"){
12350                 return this.addFill();
12351             }
12352             return this.addText(el);
12353             
12354         }
12355         if(el.tagName){ // element
12356             return this.addElement(el);
12357         }
12358         if(typeof el == "object"){ // must be button config?
12359             return this.addButton(el);
12360         }
12361         // and now what?!?!
12362         return false;
12363         
12364     },
12365     
12366     /**
12367      * Add an Xtype element
12368      * @param {Object} xtype Xtype Object
12369      * @return {Object} created Object
12370      */
12371     addxtype : function(e){
12372         return this.add(e);  
12373     },
12374     
12375     /**
12376      * Returns the Element for this toolbar.
12377      * @return {Roo.Element}
12378      */
12379     getEl : function(){
12380         return this.el;  
12381     },
12382     
12383     /**
12384      * Adds a separator
12385      * @return {Roo.Toolbar.Item} The separator item
12386      */
12387     addSeparator : function(){
12388         return this.addItem(new Roo.Toolbar.Separator());
12389     },
12390
12391     /**
12392      * Adds a spacer element
12393      * @return {Roo.Toolbar.Spacer} The spacer item
12394      */
12395     addSpacer : function(){
12396         return this.addItem(new Roo.Toolbar.Spacer());
12397     },
12398
12399     /**
12400      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12401      * @return {Roo.Toolbar.Fill} The fill item
12402      */
12403     addFill : function(){
12404         return this.addItem(new Roo.Toolbar.Fill());
12405     },
12406
12407     /**
12408      * Adds any standard HTML element to the toolbar
12409      * @param {String/HTMLElement/Element} el The element or id of the element to add
12410      * @return {Roo.Toolbar.Item} The element's item
12411      */
12412     addElement : function(el){
12413         return this.addItem(new Roo.Toolbar.Item(el));
12414     },
12415     /**
12416      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12417      * @type Roo.util.MixedCollection  
12418      */
12419     items : false,
12420      
12421     /**
12422      * Adds any Toolbar.Item or subclass
12423      * @param {Roo.Toolbar.Item} item
12424      * @return {Roo.Toolbar.Item} The item
12425      */
12426     addItem : function(item){
12427         var td = this.nextBlock();
12428         item.render(td);
12429         this.items.add(item);
12430         return item;
12431     },
12432     
12433     /**
12434      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12435      * @param {Object/Array} config A button config or array of configs
12436      * @return {Roo.Toolbar.Button/Array}
12437      */
12438     addButton : function(config){
12439         if(config instanceof Array){
12440             var buttons = [];
12441             for(var i = 0, len = config.length; i < len; i++) {
12442                 buttons.push(this.addButton(config[i]));
12443             }
12444             return buttons;
12445         }
12446         var b = config;
12447         if(!(config instanceof Roo.Toolbar.Button)){
12448             b = config.split ?
12449                 new Roo.Toolbar.SplitButton(config) :
12450                 new Roo.Toolbar.Button(config);
12451         }
12452         var td = this.nextBlock();
12453         b.render(td);
12454         this.items.add(b);
12455         return b;
12456     },
12457     
12458     /**
12459      * Adds text to the toolbar
12460      * @param {String} text The text to add
12461      * @return {Roo.Toolbar.Item} The element's item
12462      */
12463     addText : function(text){
12464         return this.addItem(new Roo.Toolbar.TextItem(text));
12465     },
12466     
12467     /**
12468      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12469      * @param {Number} index The index where the item is to be inserted
12470      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12471      * @return {Roo.Toolbar.Button/Item}
12472      */
12473     insertButton : function(index, item){
12474         if(item instanceof Array){
12475             var buttons = [];
12476             for(var i = 0, len = item.length; i < len; i++) {
12477                buttons.push(this.insertButton(index + i, item[i]));
12478             }
12479             return buttons;
12480         }
12481         if (!(item instanceof Roo.Toolbar.Button)){
12482            item = new Roo.Toolbar.Button(item);
12483         }
12484         var td = document.createElement("td");
12485         this.tr.insertBefore(td, this.tr.childNodes[index]);
12486         item.render(td);
12487         this.items.insert(index, item);
12488         return item;
12489     },
12490     
12491     /**
12492      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12493      * @param {Object} config
12494      * @return {Roo.Toolbar.Item} The element's item
12495      */
12496     addDom : function(config, returnEl){
12497         var td = this.nextBlock();
12498         Roo.DomHelper.overwrite(td, config);
12499         var ti = new Roo.Toolbar.Item(td.firstChild);
12500         ti.render(td);
12501         this.items.add(ti);
12502         return ti;
12503     },
12504
12505     /**
12506      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12507      * @type Roo.util.MixedCollection  
12508      */
12509     fields : false,
12510     
12511     /**
12512      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12513      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12514      * @param {Roo.form.Field} field
12515      * @return {Roo.ToolbarItem}
12516      */
12517      
12518       
12519     addField : function(field) {
12520         if (!this.fields) {
12521             var autoId = 0;
12522             this.fields = new Roo.util.MixedCollection(false, function(o){
12523                 return o.id || ("item" + (++autoId));
12524             });
12525
12526         }
12527         
12528         var td = this.nextBlock();
12529         field.render(td);
12530         var ti = new Roo.Toolbar.Item(td.firstChild);
12531         ti.render(td);
12532         this.items.add(ti);
12533         this.fields.add(field);
12534         return ti;
12535     },
12536     /**
12537      * Hide the toolbar
12538      * @method hide
12539      */
12540      
12541       
12542     hide : function()
12543     {
12544         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12545         this.el.child('div').hide();
12546     },
12547     /**
12548      * Show the toolbar
12549      * @method show
12550      */
12551     show : function()
12552     {
12553         this.el.child('div').show();
12554     },
12555       
12556     // private
12557     nextBlock : function(){
12558         var td = document.createElement("td");
12559         this.tr.appendChild(td);
12560         return td;
12561     },
12562
12563     // private
12564     destroy : function(){
12565         if(this.items){ // rendered?
12566             Roo.destroy.apply(Roo, this.items.items);
12567         }
12568         if(this.fields){ // rendered?
12569             Roo.destroy.apply(Roo, this.fields.items);
12570         }
12571         Roo.Element.uncache(this.el, this.tr);
12572     }
12573 };
12574
12575 /**
12576  * @class Roo.Toolbar.Item
12577  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12578  * @constructor
12579  * Creates a new Item
12580  * @param {HTMLElement} el 
12581  */
12582 Roo.Toolbar.Item = function(el){
12583     this.el = Roo.getDom(el);
12584     this.id = Roo.id(this.el);
12585     this.hidden = false;
12586 };
12587
12588 Roo.Toolbar.Item.prototype = {
12589     
12590     /**
12591      * Get this item's HTML Element
12592      * @return {HTMLElement}
12593      */
12594     getEl : function(){
12595        return this.el;  
12596     },
12597
12598     // private
12599     render : function(td){
12600         this.td = td;
12601         td.appendChild(this.el);
12602     },
12603     
12604     /**
12605      * Removes and destroys this item.
12606      */
12607     destroy : function(){
12608         this.td.parentNode.removeChild(this.td);
12609     },
12610     
12611     /**
12612      * Shows this item.
12613      */
12614     show: function(){
12615         this.hidden = false;
12616         this.td.style.display = "";
12617     },
12618     
12619     /**
12620      * Hides this item.
12621      */
12622     hide: function(){
12623         this.hidden = true;
12624         this.td.style.display = "none";
12625     },
12626     
12627     /**
12628      * Convenience function for boolean show/hide.
12629      * @param {Boolean} visible true to show/false to hide
12630      */
12631     setVisible: function(visible){
12632         if(visible) {
12633             this.show();
12634         }else{
12635             this.hide();
12636         }
12637     },
12638     
12639     /**
12640      * Try to focus this item.
12641      */
12642     focus : function(){
12643         Roo.fly(this.el).focus();
12644     },
12645     
12646     /**
12647      * Disables this item.
12648      */
12649     disable : function(){
12650         Roo.fly(this.td).addClass("x-item-disabled");
12651         this.disabled = true;
12652         this.el.disabled = true;
12653     },
12654     
12655     /**
12656      * Enables this item.
12657      */
12658     enable : function(){
12659         Roo.fly(this.td).removeClass("x-item-disabled");
12660         this.disabled = false;
12661         this.el.disabled = false;
12662     }
12663 };
12664
12665
12666 /**
12667  * @class Roo.Toolbar.Separator
12668  * @extends Roo.Toolbar.Item
12669  * A simple toolbar separator class
12670  * @constructor
12671  * Creates a new Separator
12672  */
12673 Roo.Toolbar.Separator = function(){
12674     var s = document.createElement("span");
12675     s.className = "ytb-sep";
12676     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12677 };
12678 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12679     enable:Roo.emptyFn,
12680     disable:Roo.emptyFn,
12681     focus:Roo.emptyFn
12682 });
12683
12684 /**
12685  * @class Roo.Toolbar.Spacer
12686  * @extends Roo.Toolbar.Item
12687  * A simple element that adds extra horizontal space to a toolbar.
12688  * @constructor
12689  * Creates a new Spacer
12690  */
12691 Roo.Toolbar.Spacer = function(){
12692     var s = document.createElement("div");
12693     s.className = "ytb-spacer";
12694     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12695 };
12696 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12697     enable:Roo.emptyFn,
12698     disable:Roo.emptyFn,
12699     focus:Roo.emptyFn
12700 });
12701
12702 /**
12703  * @class Roo.Toolbar.Fill
12704  * @extends Roo.Toolbar.Spacer
12705  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12706  * @constructor
12707  * Creates a new Spacer
12708  */
12709 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12710     // private
12711     render : function(td){
12712         td.style.width = '100%';
12713         Roo.Toolbar.Fill.superclass.render.call(this, td);
12714     }
12715 });
12716
12717 /**
12718  * @class Roo.Toolbar.TextItem
12719  * @extends Roo.Toolbar.Item
12720  * A simple class that renders text directly into a toolbar.
12721  * @constructor
12722  * Creates a new TextItem
12723  * @param {String} text
12724  */
12725 Roo.Toolbar.TextItem = function(text){
12726     if (typeof(text) == 'object') {
12727         text = text.text;
12728     }
12729     var s = document.createElement("span");
12730     s.className = "ytb-text";
12731     s.innerHTML = text;
12732     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12733 };
12734 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12735     enable:Roo.emptyFn,
12736     disable:Roo.emptyFn,
12737     focus:Roo.emptyFn
12738 });
12739
12740 /**
12741  * @class Roo.Toolbar.Button
12742  * @extends Roo.Button
12743  * A button that renders into a toolbar.
12744  * @constructor
12745  * Creates a new Button
12746  * @param {Object} config A standard {@link Roo.Button} config object
12747  */
12748 Roo.Toolbar.Button = function(config){
12749     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12750 };
12751 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12752     render : function(td){
12753         this.td = td;
12754         Roo.Toolbar.Button.superclass.render.call(this, td);
12755     },
12756     
12757     /**
12758      * Removes and destroys this button
12759      */
12760     destroy : function(){
12761         Roo.Toolbar.Button.superclass.destroy.call(this);
12762         this.td.parentNode.removeChild(this.td);
12763     },
12764     
12765     /**
12766      * Shows this button
12767      */
12768     show: function(){
12769         this.hidden = false;
12770         this.td.style.display = "";
12771     },
12772     
12773     /**
12774      * Hides this button
12775      */
12776     hide: function(){
12777         this.hidden = true;
12778         this.td.style.display = "none";
12779     },
12780
12781     /**
12782      * Disables this item
12783      */
12784     disable : function(){
12785         Roo.fly(this.td).addClass("x-item-disabled");
12786         this.disabled = true;
12787     },
12788
12789     /**
12790      * Enables this item
12791      */
12792     enable : function(){
12793         Roo.fly(this.td).removeClass("x-item-disabled");
12794         this.disabled = false;
12795     }
12796 });
12797 // backwards compat
12798 Roo.ToolbarButton = Roo.Toolbar.Button;
12799
12800 /**
12801  * @class Roo.Toolbar.SplitButton
12802  * @extends Roo.SplitButton
12803  * A menu button that renders into a toolbar.
12804  * @constructor
12805  * Creates a new SplitButton
12806  * @param {Object} config A standard {@link Roo.SplitButton} config object
12807  */
12808 Roo.Toolbar.SplitButton = function(config){
12809     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12810 };
12811 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12812     render : function(td){
12813         this.td = td;
12814         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12815     },
12816     
12817     /**
12818      * Removes and destroys this button
12819      */
12820     destroy : function(){
12821         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12822         this.td.parentNode.removeChild(this.td);
12823     },
12824     
12825     /**
12826      * Shows this button
12827      */
12828     show: function(){
12829         this.hidden = false;
12830         this.td.style.display = "";
12831     },
12832     
12833     /**
12834      * Hides this button
12835      */
12836     hide: function(){
12837         this.hidden = true;
12838         this.td.style.display = "none";
12839     }
12840 });
12841
12842 // backwards compat
12843 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12844  * Based on:
12845  * Ext JS Library 1.1.1
12846  * Copyright(c) 2006-2007, Ext JS, LLC.
12847  *
12848  * Originally Released Under LGPL - original licence link has changed is not relivant.
12849  *
12850  * Fork - LGPL
12851  * <script type="text/javascript">
12852  */
12853  
12854 /**
12855  * @class Roo.PagingToolbar
12856  * @extends Roo.Toolbar
12857  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12858  * @constructor
12859  * Create a new PagingToolbar
12860  * @param {Object} config The config object
12861  */
12862 Roo.PagingToolbar = function(el, ds, config)
12863 {
12864     // old args format still supported... - xtype is prefered..
12865     if (typeof(el) == 'object' && el.xtype) {
12866         // created from xtype...
12867         config = el;
12868         ds = el.dataSource;
12869         el = config.container;
12870     }
12871     var items = [];
12872     if (config.items) {
12873         items = config.items;
12874         config.items = [];
12875     }
12876     
12877     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12878     this.ds = ds;
12879     this.cursor = 0;
12880     this.renderButtons(this.el);
12881     this.bind(ds);
12882     
12883     // supprot items array.
12884    
12885     Roo.each(items, function(e) {
12886         this.add(Roo.factory(e));
12887     },this);
12888     
12889 };
12890
12891 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12892     /**
12893      * @cfg {Roo.data.Store} dataSource
12894      * The underlying data store providing the paged data
12895      */
12896     /**
12897      * @cfg {String/HTMLElement/Element} container
12898      * container The id or element that will contain the toolbar
12899      */
12900     /**
12901      * @cfg {Boolean} displayInfo
12902      * True to display the displayMsg (defaults to false)
12903      */
12904     /**
12905      * @cfg {Number} pageSize
12906      * The number of records to display per page (defaults to 20)
12907      */
12908     pageSize: 20,
12909     /**
12910      * @cfg {String} displayMsg
12911      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12912      */
12913     displayMsg : 'Displaying {0} - {1} of {2}',
12914     /**
12915      * @cfg {String} emptyMsg
12916      * The message to display when no records are found (defaults to "No data to display")
12917      */
12918     emptyMsg : 'No data to display',
12919     /**
12920      * Customizable piece of the default paging text (defaults to "Page")
12921      * @type String
12922      */
12923     beforePageText : "Page",
12924     /**
12925      * Customizable piece of the default paging text (defaults to "of %0")
12926      * @type String
12927      */
12928     afterPageText : "of {0}",
12929     /**
12930      * Customizable piece of the default paging text (defaults to "First Page")
12931      * @type String
12932      */
12933     firstText : "First Page",
12934     /**
12935      * Customizable piece of the default paging text (defaults to "Previous Page")
12936      * @type String
12937      */
12938     prevText : "Previous Page",
12939     /**
12940      * Customizable piece of the default paging text (defaults to "Next Page")
12941      * @type String
12942      */
12943     nextText : "Next Page",
12944     /**
12945      * Customizable piece of the default paging text (defaults to "Last Page")
12946      * @type String
12947      */
12948     lastText : "Last Page",
12949     /**
12950      * Customizable piece of the default paging text (defaults to "Refresh")
12951      * @type String
12952      */
12953     refreshText : "Refresh",
12954
12955     // private
12956     renderButtons : function(el){
12957         Roo.PagingToolbar.superclass.render.call(this, el);
12958         this.first = this.addButton({
12959             tooltip: this.firstText,
12960             cls: "x-btn-icon x-grid-page-first",
12961             disabled: true,
12962             handler: this.onClick.createDelegate(this, ["first"])
12963         });
12964         this.prev = this.addButton({
12965             tooltip: this.prevText,
12966             cls: "x-btn-icon x-grid-page-prev",
12967             disabled: true,
12968             handler: this.onClick.createDelegate(this, ["prev"])
12969         });
12970         //this.addSeparator();
12971         this.add(this.beforePageText);
12972         this.field = Roo.get(this.addDom({
12973            tag: "input",
12974            type: "text",
12975            size: "3",
12976            value: "1",
12977            cls: "x-grid-page-number"
12978         }).el);
12979         this.field.on("keydown", this.onPagingKeydown, this);
12980         this.field.on("focus", function(){this.dom.select();});
12981         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12982         this.field.setHeight(18);
12983         //this.addSeparator();
12984         this.next = this.addButton({
12985             tooltip: this.nextText,
12986             cls: "x-btn-icon x-grid-page-next",
12987             disabled: true,
12988             handler: this.onClick.createDelegate(this, ["next"])
12989         });
12990         this.last = this.addButton({
12991             tooltip: this.lastText,
12992             cls: "x-btn-icon x-grid-page-last",
12993             disabled: true,
12994             handler: this.onClick.createDelegate(this, ["last"])
12995         });
12996         //this.addSeparator();
12997         this.loading = this.addButton({
12998             tooltip: this.refreshText,
12999             cls: "x-btn-icon x-grid-loading",
13000             handler: this.onClick.createDelegate(this, ["refresh"])
13001         });
13002
13003         if(this.displayInfo){
13004             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13005         }
13006     },
13007
13008     // private
13009     updateInfo : function(){
13010         if(this.displayEl){
13011             var count = this.ds.getCount();
13012             var msg = count == 0 ?
13013                 this.emptyMsg :
13014                 String.format(
13015                     this.displayMsg,
13016                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13017                 );
13018             this.displayEl.update(msg);
13019         }
13020     },
13021
13022     // private
13023     onLoad : function(ds, r, o){
13024        this.cursor = o.params ? o.params.start : 0;
13025        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13026
13027        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13028        this.field.dom.value = ap;
13029        this.first.setDisabled(ap == 1);
13030        this.prev.setDisabled(ap == 1);
13031        this.next.setDisabled(ap == ps);
13032        this.last.setDisabled(ap == ps);
13033        this.loading.enable();
13034        this.updateInfo();
13035     },
13036
13037     // private
13038     getPageData : function(){
13039         var total = this.ds.getTotalCount();
13040         return {
13041             total : total,
13042             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13043             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13044         };
13045     },
13046
13047     // private
13048     onLoadError : function(){
13049         this.loading.enable();
13050     },
13051
13052     // private
13053     onPagingKeydown : function(e){
13054         var k = e.getKey();
13055         var d = this.getPageData();
13056         if(k == e.RETURN){
13057             var v = this.field.dom.value, pageNum;
13058             if(!v || isNaN(pageNum = parseInt(v, 10))){
13059                 this.field.dom.value = d.activePage;
13060                 return;
13061             }
13062             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13063             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13064             e.stopEvent();
13065         }
13066         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))
13067         {
13068           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13069           this.field.dom.value = pageNum;
13070           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13071           e.stopEvent();
13072         }
13073         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13074         {
13075           var v = this.field.dom.value, pageNum; 
13076           var increment = (e.shiftKey) ? 10 : 1;
13077           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13078             increment *= -1;
13079           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13080             this.field.dom.value = d.activePage;
13081             return;
13082           }
13083           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13084           {
13085             this.field.dom.value = parseInt(v, 10) + increment;
13086             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13087             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13088           }
13089           e.stopEvent();
13090         }
13091     },
13092
13093     // private
13094     beforeLoad : function(){
13095         if(this.loading){
13096             this.loading.disable();
13097         }
13098     },
13099
13100     // private
13101     onClick : function(which){
13102         var ds = this.ds;
13103         switch(which){
13104             case "first":
13105                 ds.load({params:{start: 0, limit: this.pageSize}});
13106             break;
13107             case "prev":
13108                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13109             break;
13110             case "next":
13111                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13112             break;
13113             case "last":
13114                 var total = ds.getTotalCount();
13115                 var extra = total % this.pageSize;
13116                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13117                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13118             break;
13119             case "refresh":
13120                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13121             break;
13122         }
13123     },
13124
13125     /**
13126      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13127      * @param {Roo.data.Store} store The data store to unbind
13128      */
13129     unbind : function(ds){
13130         ds.un("beforeload", this.beforeLoad, this);
13131         ds.un("load", this.onLoad, this);
13132         ds.un("loadexception", this.onLoadError, this);
13133         ds.un("remove", this.updateInfo, this);
13134         ds.un("add", this.updateInfo, this);
13135         this.ds = undefined;
13136     },
13137
13138     /**
13139      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13140      * @param {Roo.data.Store} store The data store to bind
13141      */
13142     bind : function(ds){
13143         ds.on("beforeload", this.beforeLoad, this);
13144         ds.on("load", this.onLoad, this);
13145         ds.on("loadexception", this.onLoadError, this);
13146         ds.on("remove", this.updateInfo, this);
13147         ds.on("add", this.updateInfo, this);
13148         this.ds = ds;
13149     }
13150 });/*
13151  * Based on:
13152  * Ext JS Library 1.1.1
13153  * Copyright(c) 2006-2007, Ext JS, LLC.
13154  *
13155  * Originally Released Under LGPL - original licence link has changed is not relivant.
13156  *
13157  * Fork - LGPL
13158  * <script type="text/javascript">
13159  */
13160
13161 /**
13162  * @class Roo.Resizable
13163  * @extends Roo.util.Observable
13164  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13165  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13166  * 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
13167  * the element will be wrapped for you automatically.</p>
13168  * <p>Here is the list of valid resize handles:</p>
13169  * <pre>
13170 Value   Description
13171 ------  -------------------
13172  'n'     north
13173  's'     south
13174  'e'     east
13175  'w'     west
13176  'nw'    northwest
13177  'sw'    southwest
13178  'se'    southeast
13179  'ne'    northeast
13180  'hd'    horizontal drag
13181  'all'   all
13182 </pre>
13183  * <p>Here's an example showing the creation of a typical Resizable:</p>
13184  * <pre><code>
13185 var resizer = new Roo.Resizable("element-id", {
13186     handles: 'all',
13187     minWidth: 200,
13188     minHeight: 100,
13189     maxWidth: 500,
13190     maxHeight: 400,
13191     pinned: true
13192 });
13193 resizer.on("resize", myHandler);
13194 </code></pre>
13195  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13196  * resizer.east.setDisplayed(false);</p>
13197  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13198  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13199  * resize operation's new size (defaults to [0, 0])
13200  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13201  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13202  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13203  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13204  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13205  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13206  * @cfg {Number} width The width of the element in pixels (defaults to null)
13207  * @cfg {Number} height The height of the element in pixels (defaults to null)
13208  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13209  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13210  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13211  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13212  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13213  * in favor of the handles config option (defaults to false)
13214  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13215  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13216  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13217  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13218  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13219  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13220  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13221  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13222  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13223  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13224  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13225  * @constructor
13226  * Create a new resizable component
13227  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13228  * @param {Object} config configuration options
13229   */
13230 Roo.Resizable = function(el, config)
13231 {
13232     this.el = Roo.get(el);
13233
13234     if(config && config.wrap){
13235         config.resizeChild = this.el;
13236         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13237         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13238         this.el.setStyle("overflow", "hidden");
13239         this.el.setPositioning(config.resizeChild.getPositioning());
13240         config.resizeChild.clearPositioning();
13241         if(!config.width || !config.height){
13242             var csize = config.resizeChild.getSize();
13243             this.el.setSize(csize.width, csize.height);
13244         }
13245         if(config.pinned && !config.adjustments){
13246             config.adjustments = "auto";
13247         }
13248     }
13249
13250     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13251     this.proxy.unselectable();
13252     this.proxy.enableDisplayMode('block');
13253
13254     Roo.apply(this, config);
13255
13256     if(this.pinned){
13257         this.disableTrackOver = true;
13258         this.el.addClass("x-resizable-pinned");
13259     }
13260     // if the element isn't positioned, make it relative
13261     var position = this.el.getStyle("position");
13262     if(position != "absolute" && position != "fixed"){
13263         this.el.setStyle("position", "relative");
13264     }
13265     if(!this.handles){ // no handles passed, must be legacy style
13266         this.handles = 's,e,se';
13267         if(this.multiDirectional){
13268             this.handles += ',n,w';
13269         }
13270     }
13271     if(this.handles == "all"){
13272         this.handles = "n s e w ne nw se sw";
13273     }
13274     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13275     var ps = Roo.Resizable.positions;
13276     for(var i = 0, len = hs.length; i < len; i++){
13277         if(hs[i] && ps[hs[i]]){
13278             var pos = ps[hs[i]];
13279             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13280         }
13281     }
13282     // legacy
13283     this.corner = this.southeast;
13284     
13285     // updateBox = the box can move..
13286     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13287         this.updateBox = true;
13288     }
13289
13290     this.activeHandle = null;
13291
13292     if(this.resizeChild){
13293         if(typeof this.resizeChild == "boolean"){
13294             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13295         }else{
13296             this.resizeChild = Roo.get(this.resizeChild, true);
13297         }
13298     }
13299     
13300     if(this.adjustments == "auto"){
13301         var rc = this.resizeChild;
13302         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13303         if(rc && (hw || hn)){
13304             rc.position("relative");
13305             rc.setLeft(hw ? hw.el.getWidth() : 0);
13306             rc.setTop(hn ? hn.el.getHeight() : 0);
13307         }
13308         this.adjustments = [
13309             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13310             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13311         ];
13312     }
13313
13314     if(this.draggable){
13315         this.dd = this.dynamic ?
13316             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13317         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13318     }
13319
13320     // public events
13321     this.addEvents({
13322         /**
13323          * @event beforeresize
13324          * Fired before resize is allowed. Set enabled to false to cancel resize.
13325          * @param {Roo.Resizable} this
13326          * @param {Roo.EventObject} e The mousedown event
13327          */
13328         "beforeresize" : true,
13329         /**
13330          * @event resize
13331          * Fired after a resize.
13332          * @param {Roo.Resizable} this
13333          * @param {Number} width The new width
13334          * @param {Number} height The new height
13335          * @param {Roo.EventObject} e The mouseup event
13336          */
13337         "resize" : true
13338     });
13339
13340     if(this.width !== null && this.height !== null){
13341         this.resizeTo(this.width, this.height);
13342     }else{
13343         this.updateChildSize();
13344     }
13345     if(Roo.isIE){
13346         this.el.dom.style.zoom = 1;
13347     }
13348     Roo.Resizable.superclass.constructor.call(this);
13349 };
13350
13351 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13352         resizeChild : false,
13353         adjustments : [0, 0],
13354         minWidth : 5,
13355         minHeight : 5,
13356         maxWidth : 10000,
13357         maxHeight : 10000,
13358         enabled : true,
13359         animate : false,
13360         duration : .35,
13361         dynamic : false,
13362         handles : false,
13363         multiDirectional : false,
13364         disableTrackOver : false,
13365         easing : 'easeOutStrong',
13366         widthIncrement : 0,
13367         heightIncrement : 0,
13368         pinned : false,
13369         width : null,
13370         height : null,
13371         preserveRatio : false,
13372         transparent: false,
13373         minX: 0,
13374         minY: 0,
13375         draggable: false,
13376
13377         /**
13378          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13379          */
13380         constrainTo: undefined,
13381         /**
13382          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13383          */
13384         resizeRegion: undefined,
13385
13386
13387     /**
13388      * Perform a manual resize
13389      * @param {Number} width
13390      * @param {Number} height
13391      */
13392     resizeTo : function(width, height){
13393         this.el.setSize(width, height);
13394         this.updateChildSize();
13395         this.fireEvent("resize", this, width, height, null);
13396     },
13397
13398     // private
13399     startSizing : function(e, handle){
13400         this.fireEvent("beforeresize", this, e);
13401         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13402
13403             if(!this.overlay){
13404                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13405                 this.overlay.unselectable();
13406                 this.overlay.enableDisplayMode("block");
13407                 this.overlay.on("mousemove", this.onMouseMove, this);
13408                 this.overlay.on("mouseup", this.onMouseUp, this);
13409             }
13410             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13411
13412             this.resizing = true;
13413             this.startBox = this.el.getBox();
13414             this.startPoint = e.getXY();
13415             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13416                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13417
13418             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13419             this.overlay.show();
13420
13421             if(this.constrainTo) {
13422                 var ct = Roo.get(this.constrainTo);
13423                 this.resizeRegion = ct.getRegion().adjust(
13424                     ct.getFrameWidth('t'),
13425                     ct.getFrameWidth('l'),
13426                     -ct.getFrameWidth('b'),
13427                     -ct.getFrameWidth('r')
13428                 );
13429             }
13430
13431             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13432             this.proxy.show();
13433             this.proxy.setBox(this.startBox);
13434             if(!this.dynamic){
13435                 this.proxy.setStyle('visibility', 'visible');
13436             }
13437         }
13438     },
13439
13440     // private
13441     onMouseDown : function(handle, e){
13442         if(this.enabled){
13443             e.stopEvent();
13444             this.activeHandle = handle;
13445             this.startSizing(e, handle);
13446         }
13447     },
13448
13449     // private
13450     onMouseUp : function(e){
13451         var size = this.resizeElement();
13452         this.resizing = false;
13453         this.handleOut();
13454         this.overlay.hide();
13455         this.proxy.hide();
13456         this.fireEvent("resize", this, size.width, size.height, e);
13457     },
13458
13459     // private
13460     updateChildSize : function(){
13461         if(this.resizeChild){
13462             var el = this.el;
13463             var child = this.resizeChild;
13464             var adj = this.adjustments;
13465             if(el.dom.offsetWidth){
13466                 var b = el.getSize(true);
13467                 child.setSize(b.width+adj[0], b.height+adj[1]);
13468             }
13469             // Second call here for IE
13470             // The first call enables instant resizing and
13471             // the second call corrects scroll bars if they
13472             // exist
13473             if(Roo.isIE){
13474                 setTimeout(function(){
13475                     if(el.dom.offsetWidth){
13476                         var b = el.getSize(true);
13477                         child.setSize(b.width+adj[0], b.height+adj[1]);
13478                     }
13479                 }, 10);
13480             }
13481         }
13482     },
13483
13484     // private
13485     snap : function(value, inc, min){
13486         if(!inc || !value) return value;
13487         var newValue = value;
13488         var m = value % inc;
13489         if(m > 0){
13490             if(m > (inc/2)){
13491                 newValue = value + (inc-m);
13492             }else{
13493                 newValue = value - m;
13494             }
13495         }
13496         return Math.max(min, newValue);
13497     },
13498
13499     // private
13500     resizeElement : function(){
13501         var box = this.proxy.getBox();
13502         if(this.updateBox){
13503             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13504         }else{
13505             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13506         }
13507         this.updateChildSize();
13508         if(!this.dynamic){
13509             this.proxy.hide();
13510         }
13511         return box;
13512     },
13513
13514     // private
13515     constrain : function(v, diff, m, mx){
13516         if(v - diff < m){
13517             diff = v - m;
13518         }else if(v - diff > mx){
13519             diff = mx - v;
13520         }
13521         return diff;
13522     },
13523
13524     // private
13525     onMouseMove : function(e){
13526         if(this.enabled){
13527             try{// try catch so if something goes wrong the user doesn't get hung
13528
13529             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13530                 return;
13531             }
13532
13533             //var curXY = this.startPoint;
13534             var curSize = this.curSize || this.startBox;
13535             var x = this.startBox.x, y = this.startBox.y;
13536             var ox = x, oy = y;
13537             var w = curSize.width, h = curSize.height;
13538             var ow = w, oh = h;
13539             var mw = this.minWidth, mh = this.minHeight;
13540             var mxw = this.maxWidth, mxh = this.maxHeight;
13541             var wi = this.widthIncrement;
13542             var hi = this.heightIncrement;
13543
13544             var eventXY = e.getXY();
13545             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13546             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13547
13548             var pos = this.activeHandle.position;
13549
13550             switch(pos){
13551                 case "east":
13552                     w += diffX;
13553                     w = Math.min(Math.max(mw, w), mxw);
13554                     break;
13555              
13556                 case "south":
13557                     h += diffY;
13558                     h = Math.min(Math.max(mh, h), mxh);
13559                     break;
13560                 case "southeast":
13561                     w += diffX;
13562                     h += diffY;
13563                     w = Math.min(Math.max(mw, w), mxw);
13564                     h = Math.min(Math.max(mh, h), mxh);
13565                     break;
13566                 case "north":
13567                     diffY = this.constrain(h, diffY, mh, mxh);
13568                     y += diffY;
13569                     h -= diffY;
13570                     break;
13571                 case "hdrag":
13572                     
13573                     if (wi) {
13574                         var adiffX = Math.abs(diffX);
13575                         var sub = (adiffX % wi); // how much 
13576                         if (sub > (wi/2)) { // far enough to snap
13577                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13578                         } else {
13579                             // remove difference.. 
13580                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13581                         }
13582                     }
13583                     x += diffX;
13584                     x = Math.max(this.minX, x);
13585                     break;
13586                 case "west":
13587                     diffX = this.constrain(w, diffX, mw, mxw);
13588                     x += diffX;
13589                     w -= diffX;
13590                     break;
13591                 case "northeast":
13592                     w += diffX;
13593                     w = Math.min(Math.max(mw, w), mxw);
13594                     diffY = this.constrain(h, diffY, mh, mxh);
13595                     y += diffY;
13596                     h -= diffY;
13597                     break;
13598                 case "northwest":
13599                     diffX = this.constrain(w, diffX, mw, mxw);
13600                     diffY = this.constrain(h, diffY, mh, mxh);
13601                     y += diffY;
13602                     h -= diffY;
13603                     x += diffX;
13604                     w -= diffX;
13605                     break;
13606                case "southwest":
13607                     diffX = this.constrain(w, diffX, mw, mxw);
13608                     h += diffY;
13609                     h = Math.min(Math.max(mh, h), mxh);
13610                     x += diffX;
13611                     w -= diffX;
13612                     break;
13613             }
13614
13615             var sw = this.snap(w, wi, mw);
13616             var sh = this.snap(h, hi, mh);
13617             if(sw != w || sh != h){
13618                 switch(pos){
13619                     case "northeast":
13620                         y -= sh - h;
13621                     break;
13622                     case "north":
13623                         y -= sh - h;
13624                         break;
13625                     case "southwest":
13626                         x -= sw - w;
13627                     break;
13628                     case "west":
13629                         x -= sw - w;
13630                         break;
13631                     case "northwest":
13632                         x -= sw - w;
13633                         y -= sh - h;
13634                     break;
13635                 }
13636                 w = sw;
13637                 h = sh;
13638             }
13639
13640             if(this.preserveRatio){
13641                 switch(pos){
13642                     case "southeast":
13643                     case "east":
13644                         h = oh * (w/ow);
13645                         h = Math.min(Math.max(mh, h), mxh);
13646                         w = ow * (h/oh);
13647                        break;
13648                     case "south":
13649                         w = ow * (h/oh);
13650                         w = Math.min(Math.max(mw, w), mxw);
13651                         h = oh * (w/ow);
13652                         break;
13653                     case "northeast":
13654                         w = ow * (h/oh);
13655                         w = Math.min(Math.max(mw, w), mxw);
13656                         h = oh * (w/ow);
13657                     break;
13658                     case "north":
13659                         var tw = w;
13660                         w = ow * (h/oh);
13661                         w = Math.min(Math.max(mw, w), mxw);
13662                         h = oh * (w/ow);
13663                         x += (tw - w) / 2;
13664                         break;
13665                     case "southwest":
13666                         h = oh * (w/ow);
13667                         h = Math.min(Math.max(mh, h), mxh);
13668                         var tw = w;
13669                         w = ow * (h/oh);
13670                         x += tw - w;
13671                         break;
13672                     case "west":
13673                         var th = h;
13674                         h = oh * (w/ow);
13675                         h = Math.min(Math.max(mh, h), mxh);
13676                         y += (th - h) / 2;
13677                         var tw = w;
13678                         w = ow * (h/oh);
13679                         x += tw - w;
13680                        break;
13681                     case "northwest":
13682                         var tw = w;
13683                         var th = h;
13684                         h = oh * (w/ow);
13685                         h = Math.min(Math.max(mh, h), mxh);
13686                         w = ow * (h/oh);
13687                         y += th - h;
13688                         x += tw - w;
13689                        break;
13690
13691                 }
13692             }
13693             if (pos == 'hdrag') {
13694                 w = ow;
13695             }
13696             this.proxy.setBounds(x, y, w, h);
13697             if(this.dynamic){
13698                 this.resizeElement();
13699             }
13700             }catch(e){}
13701         }
13702     },
13703
13704     // private
13705     handleOver : function(){
13706         if(this.enabled){
13707             this.el.addClass("x-resizable-over");
13708         }
13709     },
13710
13711     // private
13712     handleOut : function(){
13713         if(!this.resizing){
13714             this.el.removeClass("x-resizable-over");
13715         }
13716     },
13717
13718     /**
13719      * Returns the element this component is bound to.
13720      * @return {Roo.Element}
13721      */
13722     getEl : function(){
13723         return this.el;
13724     },
13725
13726     /**
13727      * Returns the resizeChild element (or null).
13728      * @return {Roo.Element}
13729      */
13730     getResizeChild : function(){
13731         return this.resizeChild;
13732     },
13733
13734     /**
13735      * Destroys this resizable. If the element was wrapped and
13736      * removeEl is not true then the element remains.
13737      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13738      */
13739     destroy : function(removeEl){
13740         this.proxy.remove();
13741         if(this.overlay){
13742             this.overlay.removeAllListeners();
13743             this.overlay.remove();
13744         }
13745         var ps = Roo.Resizable.positions;
13746         for(var k in ps){
13747             if(typeof ps[k] != "function" && this[ps[k]]){
13748                 var h = this[ps[k]];
13749                 h.el.removeAllListeners();
13750                 h.el.remove();
13751             }
13752         }
13753         if(removeEl){
13754             this.el.update("");
13755             this.el.remove();
13756         }
13757     }
13758 });
13759
13760 // private
13761 // hash to map config positions to true positions
13762 Roo.Resizable.positions = {
13763     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13764     hd: "hdrag"
13765 };
13766
13767 // private
13768 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13769     if(!this.tpl){
13770         // only initialize the template if resizable is used
13771         var tpl = Roo.DomHelper.createTemplate(
13772             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13773         );
13774         tpl.compile();
13775         Roo.Resizable.Handle.prototype.tpl = tpl;
13776     }
13777     this.position = pos;
13778     this.rz = rz;
13779     // show north drag fro topdra
13780     var handlepos = pos == 'hdrag' ? 'north' : pos;
13781     
13782     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13783     if (pos == 'hdrag') {
13784         this.el.setStyle('cursor', 'pointer');
13785     }
13786     this.el.unselectable();
13787     if(transparent){
13788         this.el.setOpacity(0);
13789     }
13790     this.el.on("mousedown", this.onMouseDown, this);
13791     if(!disableTrackOver){
13792         this.el.on("mouseover", this.onMouseOver, this);
13793         this.el.on("mouseout", this.onMouseOut, this);
13794     }
13795 };
13796
13797 // private
13798 Roo.Resizable.Handle.prototype = {
13799     afterResize : function(rz){
13800         // do nothing
13801     },
13802     // private
13803     onMouseDown : function(e){
13804         this.rz.onMouseDown(this, e);
13805     },
13806     // private
13807     onMouseOver : function(e){
13808         this.rz.handleOver(this, e);
13809     },
13810     // private
13811     onMouseOut : function(e){
13812         this.rz.handleOut(this, e);
13813     }
13814 };/*
13815  * Based on:
13816  * Ext JS Library 1.1.1
13817  * Copyright(c) 2006-2007, Ext JS, LLC.
13818  *
13819  * Originally Released Under LGPL - original licence link has changed is not relivant.
13820  *
13821  * Fork - LGPL
13822  * <script type="text/javascript">
13823  */
13824
13825 /**
13826  * @class Roo.Editor
13827  * @extends Roo.Component
13828  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13829  * @constructor
13830  * Create a new Editor
13831  * @param {Roo.form.Field} field The Field object (or descendant)
13832  * @param {Object} config The config object
13833  */
13834 Roo.Editor = function(field, config){
13835     Roo.Editor.superclass.constructor.call(this, config);
13836     this.field = field;
13837     this.addEvents({
13838         /**
13839              * @event beforestartedit
13840              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13841              * false from the handler of this event.
13842              * @param {Editor} this
13843              * @param {Roo.Element} boundEl The underlying element bound to this editor
13844              * @param {Mixed} value The field value being set
13845              */
13846         "beforestartedit" : true,
13847         /**
13848              * @event startedit
13849              * Fires when this editor is displayed
13850              * @param {Roo.Element} boundEl The underlying element bound to this editor
13851              * @param {Mixed} value The starting field value
13852              */
13853         "startedit" : true,
13854         /**
13855              * @event beforecomplete
13856              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13857              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13858              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13859              * event will not fire since no edit actually occurred.
13860              * @param {Editor} this
13861              * @param {Mixed} value The current field value
13862              * @param {Mixed} startValue The original field value
13863              */
13864         "beforecomplete" : true,
13865         /**
13866              * @event complete
13867              * Fires after editing is complete and any changed value has been written to the underlying field.
13868              * @param {Editor} this
13869              * @param {Mixed} value The current field value
13870              * @param {Mixed} startValue The original field value
13871              */
13872         "complete" : true,
13873         /**
13874          * @event specialkey
13875          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13876          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13877          * @param {Roo.form.Field} this
13878          * @param {Roo.EventObject} e The event object
13879          */
13880         "specialkey" : true
13881     });
13882 };
13883
13884 Roo.extend(Roo.Editor, Roo.Component, {
13885     /**
13886      * @cfg {Boolean/String} autosize
13887      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13888      * or "height" to adopt the height only (defaults to false)
13889      */
13890     /**
13891      * @cfg {Boolean} revertInvalid
13892      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13893      * validation fails (defaults to true)
13894      */
13895     /**
13896      * @cfg {Boolean} ignoreNoChange
13897      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13898      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13899      * will never be ignored.
13900      */
13901     /**
13902      * @cfg {Boolean} hideEl
13903      * False to keep the bound element visible while the editor is displayed (defaults to true)
13904      */
13905     /**
13906      * @cfg {Mixed} value
13907      * The data value of the underlying field (defaults to "")
13908      */
13909     value : "",
13910     /**
13911      * @cfg {String} alignment
13912      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13913      */
13914     alignment: "c-c?",
13915     /**
13916      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13917      * for bottom-right shadow (defaults to "frame")
13918      */
13919     shadow : "frame",
13920     /**
13921      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13922      */
13923     constrain : false,
13924     /**
13925      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13926      */
13927     completeOnEnter : false,
13928     /**
13929      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13930      */
13931     cancelOnEsc : false,
13932     /**
13933      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13934      */
13935     updateEl : false,
13936
13937     // private
13938     onRender : function(ct, position){
13939         this.el = new Roo.Layer({
13940             shadow: this.shadow,
13941             cls: "x-editor",
13942             parentEl : ct,
13943             shim : this.shim,
13944             shadowOffset:4,
13945             id: this.id,
13946             constrain: this.constrain
13947         });
13948         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13949         if(this.field.msgTarget != 'title'){
13950             this.field.msgTarget = 'qtip';
13951         }
13952         this.field.render(this.el);
13953         if(Roo.isGecko){
13954             this.field.el.dom.setAttribute('autocomplete', 'off');
13955         }
13956         this.field.on("specialkey", this.onSpecialKey, this);
13957         if(this.swallowKeys){
13958             this.field.el.swallowEvent(['keydown','keypress']);
13959         }
13960         this.field.show();
13961         this.field.on("blur", this.onBlur, this);
13962         if(this.field.grow){
13963             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13964         }
13965     },
13966
13967     onSpecialKey : function(field, e)
13968     {
13969         //Roo.log('editor onSpecialKey');
13970         if(this.completeOnEnter && e.getKey() == e.ENTER){
13971             e.stopEvent();
13972             this.completeEdit();
13973             return;
13974         }
13975         // do not fire special key otherwise it might hide close the editor...
13976         if(e.getKey() == e.ENTER){    
13977             return;
13978         }
13979         if(this.cancelOnEsc && e.getKey() == e.ESC){
13980             this.cancelEdit();
13981             return;
13982         } 
13983         this.fireEvent('specialkey', field, e);
13984     
13985     },
13986
13987     /**
13988      * Starts the editing process and shows the editor.
13989      * @param {String/HTMLElement/Element} el The element to edit
13990      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13991       * to the innerHTML of el.
13992      */
13993     startEdit : function(el, value){
13994         if(this.editing){
13995             this.completeEdit();
13996         }
13997         this.boundEl = Roo.get(el);
13998         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13999         if(!this.rendered){
14000             this.render(this.parentEl || document.body);
14001         }
14002         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14003             return;
14004         }
14005         this.startValue = v;
14006         this.field.setValue(v);
14007         if(this.autoSize){
14008             var sz = this.boundEl.getSize();
14009             switch(this.autoSize){
14010                 case "width":
14011                 this.setSize(sz.width,  "");
14012                 break;
14013                 case "height":
14014                 this.setSize("",  sz.height);
14015                 break;
14016                 default:
14017                 this.setSize(sz.width,  sz.height);
14018             }
14019         }
14020         this.el.alignTo(this.boundEl, this.alignment);
14021         this.editing = true;
14022         if(Roo.QuickTips){
14023             Roo.QuickTips.disable();
14024         }
14025         this.show();
14026     },
14027
14028     /**
14029      * Sets the height and width of this editor.
14030      * @param {Number} width The new width
14031      * @param {Number} height The new height
14032      */
14033     setSize : function(w, h){
14034         this.field.setSize(w, h);
14035         if(this.el){
14036             this.el.sync();
14037         }
14038     },
14039
14040     /**
14041      * Realigns the editor to the bound field based on the current alignment config value.
14042      */
14043     realign : function(){
14044         this.el.alignTo(this.boundEl, this.alignment);
14045     },
14046
14047     /**
14048      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14049      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14050      */
14051     completeEdit : function(remainVisible){
14052         if(!this.editing){
14053             return;
14054         }
14055         var v = this.getValue();
14056         if(this.revertInvalid !== false && !this.field.isValid()){
14057             v = this.startValue;
14058             this.cancelEdit(true);
14059         }
14060         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14061             this.editing = false;
14062             this.hide();
14063             return;
14064         }
14065         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14066             this.editing = false;
14067             if(this.updateEl && this.boundEl){
14068                 this.boundEl.update(v);
14069             }
14070             if(remainVisible !== true){
14071                 this.hide();
14072             }
14073             this.fireEvent("complete", this, v, this.startValue);
14074         }
14075     },
14076
14077     // private
14078     onShow : function(){
14079         this.el.show();
14080         if(this.hideEl !== false){
14081             this.boundEl.hide();
14082         }
14083         this.field.show();
14084         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14085             this.fixIEFocus = true;
14086             this.deferredFocus.defer(50, this);
14087         }else{
14088             this.field.focus();
14089         }
14090         this.fireEvent("startedit", this.boundEl, this.startValue);
14091     },
14092
14093     deferredFocus : function(){
14094         if(this.editing){
14095             this.field.focus();
14096         }
14097     },
14098
14099     /**
14100      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14101      * reverted to the original starting value.
14102      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14103      * cancel (defaults to false)
14104      */
14105     cancelEdit : function(remainVisible){
14106         if(this.editing){
14107             this.setValue(this.startValue);
14108             if(remainVisible !== true){
14109                 this.hide();
14110             }
14111         }
14112     },
14113
14114     // private
14115     onBlur : function(){
14116         if(this.allowBlur !== true && this.editing){
14117             this.completeEdit();
14118         }
14119     },
14120
14121     // private
14122     onHide : function(){
14123         if(this.editing){
14124             this.completeEdit();
14125             return;
14126         }
14127         this.field.blur();
14128         if(this.field.collapse){
14129             this.field.collapse();
14130         }
14131         this.el.hide();
14132         if(this.hideEl !== false){
14133             this.boundEl.show();
14134         }
14135         if(Roo.QuickTips){
14136             Roo.QuickTips.enable();
14137         }
14138     },
14139
14140     /**
14141      * Sets the data value of the editor
14142      * @param {Mixed} value Any valid value supported by the underlying field
14143      */
14144     setValue : function(v){
14145         this.field.setValue(v);
14146     },
14147
14148     /**
14149      * Gets the data value of the editor
14150      * @return {Mixed} The data value
14151      */
14152     getValue : function(){
14153         return this.field.getValue();
14154     }
14155 });/*
14156  * Based on:
14157  * Ext JS Library 1.1.1
14158  * Copyright(c) 2006-2007, Ext JS, LLC.
14159  *
14160  * Originally Released Under LGPL - original licence link has changed is not relivant.
14161  *
14162  * Fork - LGPL
14163  * <script type="text/javascript">
14164  */
14165  
14166 /**
14167  * @class Roo.BasicDialog
14168  * @extends Roo.util.Observable
14169  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14170  * <pre><code>
14171 var dlg = new Roo.BasicDialog("my-dlg", {
14172     height: 200,
14173     width: 300,
14174     minHeight: 100,
14175     minWidth: 150,
14176     modal: true,
14177     proxyDrag: true,
14178     shadow: true
14179 });
14180 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14181 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14182 dlg.addButton('Cancel', dlg.hide, dlg);
14183 dlg.show();
14184 </code></pre>
14185   <b>A Dialog should always be a direct child of the body element.</b>
14186  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14187  * @cfg {String} title Default text to display in the title bar (defaults to null)
14188  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14189  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14190  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14191  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14192  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14193  * (defaults to null with no animation)
14194  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14195  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14196  * property for valid values (defaults to 'all')
14197  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14198  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14199  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14200  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14201  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14202  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14203  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14204  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14205  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14206  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14207  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14208  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14209  * draggable = true (defaults to false)
14210  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14211  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14212  * shadow (defaults to false)
14213  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14214  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14215  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14216  * @cfg {Array} buttons Array of buttons
14217  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14218  * @constructor
14219  * Create a new BasicDialog.
14220  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14221  * @param {Object} config Configuration options
14222  */
14223 Roo.BasicDialog = function(el, config){
14224     this.el = Roo.get(el);
14225     var dh = Roo.DomHelper;
14226     if(!this.el && config && config.autoCreate){
14227         if(typeof config.autoCreate == "object"){
14228             if(!config.autoCreate.id){
14229                 config.autoCreate.id = el;
14230             }
14231             this.el = dh.append(document.body,
14232                         config.autoCreate, true);
14233         }else{
14234             this.el = dh.append(document.body,
14235                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14236         }
14237     }
14238     el = this.el;
14239     el.setDisplayed(true);
14240     el.hide = this.hideAction;
14241     this.id = el.id;
14242     el.addClass("x-dlg");
14243
14244     Roo.apply(this, config);
14245
14246     this.proxy = el.createProxy("x-dlg-proxy");
14247     this.proxy.hide = this.hideAction;
14248     this.proxy.setOpacity(.5);
14249     this.proxy.hide();
14250
14251     if(config.width){
14252         el.setWidth(config.width);
14253     }
14254     if(config.height){
14255         el.setHeight(config.height);
14256     }
14257     this.size = el.getSize();
14258     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14259         this.xy = [config.x,config.y];
14260     }else{
14261         this.xy = el.getCenterXY(true);
14262     }
14263     /** The header element @type Roo.Element */
14264     this.header = el.child("> .x-dlg-hd");
14265     /** The body element @type Roo.Element */
14266     this.body = el.child("> .x-dlg-bd");
14267     /** The footer element @type Roo.Element */
14268     this.footer = el.child("> .x-dlg-ft");
14269
14270     if(!this.header){
14271         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14272     }
14273     if(!this.body){
14274         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14275     }
14276
14277     this.header.unselectable();
14278     if(this.title){
14279         this.header.update(this.title);
14280     }
14281     // this element allows the dialog to be focused for keyboard event
14282     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14283     this.focusEl.swallowEvent("click", true);
14284
14285     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14286
14287     // wrap the body and footer for special rendering
14288     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14289     if(this.footer){
14290         this.bwrap.dom.appendChild(this.footer.dom);
14291     }
14292
14293     this.bg = this.el.createChild({
14294         tag: "div", cls:"x-dlg-bg",
14295         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14296     });
14297     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14298
14299
14300     if(this.autoScroll !== false && !this.autoTabs){
14301         this.body.setStyle("overflow", "auto");
14302     }
14303
14304     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14305
14306     if(this.closable !== false){
14307         this.el.addClass("x-dlg-closable");
14308         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14309         this.close.on("click", this.closeClick, this);
14310         this.close.addClassOnOver("x-dlg-close-over");
14311     }
14312     if(this.collapsible !== false){
14313         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14314         this.collapseBtn.on("click", this.collapseClick, this);
14315         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14316         this.header.on("dblclick", this.collapseClick, this);
14317     }
14318     if(this.resizable !== false){
14319         this.el.addClass("x-dlg-resizable");
14320         this.resizer = new Roo.Resizable(el, {
14321             minWidth: this.minWidth || 80,
14322             minHeight:this.minHeight || 80,
14323             handles: this.resizeHandles || "all",
14324             pinned: true
14325         });
14326         this.resizer.on("beforeresize", this.beforeResize, this);
14327         this.resizer.on("resize", this.onResize, this);
14328     }
14329     if(this.draggable !== false){
14330         el.addClass("x-dlg-draggable");
14331         if (!this.proxyDrag) {
14332             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14333         }
14334         else {
14335             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14336         }
14337         dd.setHandleElId(this.header.id);
14338         dd.endDrag = this.endMove.createDelegate(this);
14339         dd.startDrag = this.startMove.createDelegate(this);
14340         dd.onDrag = this.onDrag.createDelegate(this);
14341         dd.scroll = false;
14342         this.dd = dd;
14343     }
14344     if(this.modal){
14345         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14346         this.mask.enableDisplayMode("block");
14347         this.mask.hide();
14348         this.el.addClass("x-dlg-modal");
14349     }
14350     if(this.shadow){
14351         this.shadow = new Roo.Shadow({
14352             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14353             offset : this.shadowOffset
14354         });
14355     }else{
14356         this.shadowOffset = 0;
14357     }
14358     if(Roo.useShims && this.shim !== false){
14359         this.shim = this.el.createShim();
14360         this.shim.hide = this.hideAction;
14361         this.shim.hide();
14362     }else{
14363         this.shim = false;
14364     }
14365     if(this.autoTabs){
14366         this.initTabs();
14367     }
14368     if (this.buttons) { 
14369         var bts= this.buttons;
14370         this.buttons = [];
14371         Roo.each(bts, function(b) {
14372             this.addButton(b);
14373         }, this);
14374     }
14375     
14376     
14377     this.addEvents({
14378         /**
14379          * @event keydown
14380          * Fires when a key is pressed
14381          * @param {Roo.BasicDialog} this
14382          * @param {Roo.EventObject} e
14383          */
14384         "keydown" : true,
14385         /**
14386          * @event move
14387          * Fires when this dialog is moved by the user.
14388          * @param {Roo.BasicDialog} this
14389          * @param {Number} x The new page X
14390          * @param {Number} y The new page Y
14391          */
14392         "move" : true,
14393         /**
14394          * @event resize
14395          * Fires when this dialog is resized by the user.
14396          * @param {Roo.BasicDialog} this
14397          * @param {Number} width The new width
14398          * @param {Number} height The new height
14399          */
14400         "resize" : true,
14401         /**
14402          * @event beforehide
14403          * Fires before this dialog is hidden.
14404          * @param {Roo.BasicDialog} this
14405          */
14406         "beforehide" : true,
14407         /**
14408          * @event hide
14409          * Fires when this dialog is hidden.
14410          * @param {Roo.BasicDialog} this
14411          */
14412         "hide" : true,
14413         /**
14414          * @event beforeshow
14415          * Fires before this dialog is shown.
14416          * @param {Roo.BasicDialog} this
14417          */
14418         "beforeshow" : true,
14419         /**
14420          * @event show
14421          * Fires when this dialog is shown.
14422          * @param {Roo.BasicDialog} this
14423          */
14424         "show" : true
14425     });
14426     el.on("keydown", this.onKeyDown, this);
14427     el.on("mousedown", this.toFront, this);
14428     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14429     this.el.hide();
14430     Roo.DialogManager.register(this);
14431     Roo.BasicDialog.superclass.constructor.call(this);
14432 };
14433
14434 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14435     shadowOffset: Roo.isIE ? 6 : 5,
14436     minHeight: 80,
14437     minWidth: 200,
14438     minButtonWidth: 75,
14439     defaultButton: null,
14440     buttonAlign: "right",
14441     tabTag: 'div',
14442     firstShow: true,
14443
14444     /**
14445      * Sets the dialog title text
14446      * @param {String} text The title text to display
14447      * @return {Roo.BasicDialog} this
14448      */
14449     setTitle : function(text){
14450         this.header.update(text);
14451         return this;
14452     },
14453
14454     // private
14455     closeClick : function(){
14456         this.hide();
14457     },
14458
14459     // private
14460     collapseClick : function(){
14461         this[this.collapsed ? "expand" : "collapse"]();
14462     },
14463
14464     /**
14465      * Collapses the dialog to its minimized state (only the title bar is visible).
14466      * Equivalent to the user clicking the collapse dialog button.
14467      */
14468     collapse : function(){
14469         if(!this.collapsed){
14470             this.collapsed = true;
14471             this.el.addClass("x-dlg-collapsed");
14472             this.restoreHeight = this.el.getHeight();
14473             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14474         }
14475     },
14476
14477     /**
14478      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14479      * clicking the expand dialog button.
14480      */
14481     expand : function(){
14482         if(this.collapsed){
14483             this.collapsed = false;
14484             this.el.removeClass("x-dlg-collapsed");
14485             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14486         }
14487     },
14488
14489     /**
14490      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14491      * @return {Roo.TabPanel} The tabs component
14492      */
14493     initTabs : function(){
14494         var tabs = this.getTabs();
14495         while(tabs.getTab(0)){
14496             tabs.removeTab(0);
14497         }
14498         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14499             var dom = el.dom;
14500             tabs.addTab(Roo.id(dom), dom.title);
14501             dom.title = "";
14502         });
14503         tabs.activate(0);
14504         return tabs;
14505     },
14506
14507     // private
14508     beforeResize : function(){
14509         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14510     },
14511
14512     // private
14513     onResize : function(){
14514         this.refreshSize();
14515         this.syncBodyHeight();
14516         this.adjustAssets();
14517         this.focus();
14518         this.fireEvent("resize", this, this.size.width, this.size.height);
14519     },
14520
14521     // private
14522     onKeyDown : function(e){
14523         if(this.isVisible()){
14524             this.fireEvent("keydown", this, e);
14525         }
14526     },
14527
14528     /**
14529      * Resizes the dialog.
14530      * @param {Number} width
14531      * @param {Number} height
14532      * @return {Roo.BasicDialog} this
14533      */
14534     resizeTo : function(width, height){
14535         this.el.setSize(width, height);
14536         this.size = {width: width, height: height};
14537         this.syncBodyHeight();
14538         if(this.fixedcenter){
14539             this.center();
14540         }
14541         if(this.isVisible()){
14542             this.constrainXY();
14543             this.adjustAssets();
14544         }
14545         this.fireEvent("resize", this, width, height);
14546         return this;
14547     },
14548
14549
14550     /**
14551      * Resizes the dialog to fit the specified content size.
14552      * @param {Number} width
14553      * @param {Number} height
14554      * @return {Roo.BasicDialog} this
14555      */
14556     setContentSize : function(w, h){
14557         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14558         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14559         //if(!this.el.isBorderBox()){
14560             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14561             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14562         //}
14563         if(this.tabs){
14564             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14565             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14566         }
14567         this.resizeTo(w, h);
14568         return this;
14569     },
14570
14571     /**
14572      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14573      * executed in response to a particular key being pressed while the dialog is active.
14574      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14575      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14576      * @param {Function} fn The function to call
14577      * @param {Object} scope (optional) The scope of the function
14578      * @return {Roo.BasicDialog} this
14579      */
14580     addKeyListener : function(key, fn, scope){
14581         var keyCode, shift, ctrl, alt;
14582         if(typeof key == "object" && !(key instanceof Array)){
14583             keyCode = key["key"];
14584             shift = key["shift"];
14585             ctrl = key["ctrl"];
14586             alt = key["alt"];
14587         }else{
14588             keyCode = key;
14589         }
14590         var handler = function(dlg, e){
14591             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14592                 var k = e.getKey();
14593                 if(keyCode instanceof Array){
14594                     for(var i = 0, len = keyCode.length; i < len; i++){
14595                         if(keyCode[i] == k){
14596                           fn.call(scope || window, dlg, k, e);
14597                           return;
14598                         }
14599                     }
14600                 }else{
14601                     if(k == keyCode){
14602                         fn.call(scope || window, dlg, k, e);
14603                     }
14604                 }
14605             }
14606         };
14607         this.on("keydown", handler);
14608         return this;
14609     },
14610
14611     /**
14612      * Returns the TabPanel component (creates it if it doesn't exist).
14613      * Note: If you wish to simply check for the existence of tabs without creating them,
14614      * check for a null 'tabs' property.
14615      * @return {Roo.TabPanel} The tabs component
14616      */
14617     getTabs : function(){
14618         if(!this.tabs){
14619             this.el.addClass("x-dlg-auto-tabs");
14620             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14621             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14622         }
14623         return this.tabs;
14624     },
14625
14626     /**
14627      * Adds a button to the footer section of the dialog.
14628      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14629      * object or a valid Roo.DomHelper element config
14630      * @param {Function} handler The function called when the button is clicked
14631      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14632      * @return {Roo.Button} The new button
14633      */
14634     addButton : function(config, handler, scope){
14635         var dh = Roo.DomHelper;
14636         if(!this.footer){
14637             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14638         }
14639         if(!this.btnContainer){
14640             var tb = this.footer.createChild({
14641
14642                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14643                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14644             }, null, true);
14645             this.btnContainer = tb.firstChild.firstChild.firstChild;
14646         }
14647         var bconfig = {
14648             handler: handler,
14649             scope: scope,
14650             minWidth: this.minButtonWidth,
14651             hideParent:true
14652         };
14653         if(typeof config == "string"){
14654             bconfig.text = config;
14655         }else{
14656             if(config.tag){
14657                 bconfig.dhconfig = config;
14658             }else{
14659                 Roo.apply(bconfig, config);
14660             }
14661         }
14662         var fc = false;
14663         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14664             bconfig.position = Math.max(0, bconfig.position);
14665             fc = this.btnContainer.childNodes[bconfig.position];
14666         }
14667          
14668         var btn = new Roo.Button(
14669             fc ? 
14670                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14671                 : this.btnContainer.appendChild(document.createElement("td")),
14672             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14673             bconfig
14674         );
14675         this.syncBodyHeight();
14676         if(!this.buttons){
14677             /**
14678              * Array of all the buttons that have been added to this dialog via addButton
14679              * @type Array
14680              */
14681             this.buttons = [];
14682         }
14683         this.buttons.push(btn);
14684         return btn;
14685     },
14686
14687     /**
14688      * Sets the default button to be focused when the dialog is displayed.
14689      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14690      * @return {Roo.BasicDialog} this
14691      */
14692     setDefaultButton : function(btn){
14693         this.defaultButton = btn;
14694         return this;
14695     },
14696
14697     // private
14698     getHeaderFooterHeight : function(safe){
14699         var height = 0;
14700         if(this.header){
14701            height += this.header.getHeight();
14702         }
14703         if(this.footer){
14704            var fm = this.footer.getMargins();
14705             height += (this.footer.getHeight()+fm.top+fm.bottom);
14706         }
14707         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14708         height += this.centerBg.getPadding("tb");
14709         return height;
14710     },
14711
14712     // private
14713     syncBodyHeight : function(){
14714         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14715         var height = this.size.height - this.getHeaderFooterHeight(false);
14716         bd.setHeight(height-bd.getMargins("tb"));
14717         var hh = this.header.getHeight();
14718         var h = this.size.height-hh;
14719         cb.setHeight(h);
14720         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14721         bw.setHeight(h-cb.getPadding("tb"));
14722         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14723         bd.setWidth(bw.getWidth(true));
14724         if(this.tabs){
14725             this.tabs.syncHeight();
14726             if(Roo.isIE){
14727                 this.tabs.el.repaint();
14728             }
14729         }
14730     },
14731
14732     /**
14733      * Restores the previous state of the dialog if Roo.state is configured.
14734      * @return {Roo.BasicDialog} this
14735      */
14736     restoreState : function(){
14737         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14738         if(box && box.width){
14739             this.xy = [box.x, box.y];
14740             this.resizeTo(box.width, box.height);
14741         }
14742         return this;
14743     },
14744
14745     // private
14746     beforeShow : function(){
14747         this.expand();
14748         if(this.fixedcenter){
14749             this.xy = this.el.getCenterXY(true);
14750         }
14751         if(this.modal){
14752             Roo.get(document.body).addClass("x-body-masked");
14753             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14754             this.mask.show();
14755         }
14756         this.constrainXY();
14757     },
14758
14759     // private
14760     animShow : function(){
14761         var b = Roo.get(this.animateTarget).getBox();
14762         this.proxy.setSize(b.width, b.height);
14763         this.proxy.setLocation(b.x, b.y);
14764         this.proxy.show();
14765         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14766                     true, .35, this.showEl.createDelegate(this));
14767     },
14768
14769     /**
14770      * Shows the dialog.
14771      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14772      * @return {Roo.BasicDialog} this
14773      */
14774     show : function(animateTarget){
14775         if (this.fireEvent("beforeshow", this) === false){
14776             return;
14777         }
14778         if(this.syncHeightBeforeShow){
14779             this.syncBodyHeight();
14780         }else if(this.firstShow){
14781             this.firstShow = false;
14782             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14783         }
14784         this.animateTarget = animateTarget || this.animateTarget;
14785         if(!this.el.isVisible()){
14786             this.beforeShow();
14787             if(this.animateTarget && Roo.get(this.animateTarget)){
14788                 this.animShow();
14789             }else{
14790                 this.showEl();
14791             }
14792         }
14793         return this;
14794     },
14795
14796     // private
14797     showEl : function(){
14798         this.proxy.hide();
14799         this.el.setXY(this.xy);
14800         this.el.show();
14801         this.adjustAssets(true);
14802         this.toFront();
14803         this.focus();
14804         // IE peekaboo bug - fix found by Dave Fenwick
14805         if(Roo.isIE){
14806             this.el.repaint();
14807         }
14808         this.fireEvent("show", this);
14809     },
14810
14811     /**
14812      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14813      * dialog itself will receive focus.
14814      */
14815     focus : function(){
14816         if(this.defaultButton){
14817             this.defaultButton.focus();
14818         }else{
14819             this.focusEl.focus();
14820         }
14821     },
14822
14823     // private
14824     constrainXY : function(){
14825         if(this.constraintoviewport !== false){
14826             if(!this.viewSize){
14827                 if(this.container){
14828                     var s = this.container.getSize();
14829                     this.viewSize = [s.width, s.height];
14830                 }else{
14831                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14832                 }
14833             }
14834             var s = Roo.get(this.container||document).getScroll();
14835
14836             var x = this.xy[0], y = this.xy[1];
14837             var w = this.size.width, h = this.size.height;
14838             var vw = this.viewSize[0], vh = this.viewSize[1];
14839             // only move it if it needs it
14840             var moved = false;
14841             // first validate right/bottom
14842             if(x + w > vw+s.left){
14843                 x = vw - w;
14844                 moved = true;
14845             }
14846             if(y + h > vh+s.top){
14847                 y = vh - h;
14848                 moved = true;
14849             }
14850             // then make sure top/left isn't negative
14851             if(x < s.left){
14852                 x = s.left;
14853                 moved = true;
14854             }
14855             if(y < s.top){
14856                 y = s.top;
14857                 moved = true;
14858             }
14859             if(moved){
14860                 // cache xy
14861                 this.xy = [x, y];
14862                 if(this.isVisible()){
14863                     this.el.setLocation(x, y);
14864                     this.adjustAssets();
14865                 }
14866             }
14867         }
14868     },
14869
14870     // private
14871     onDrag : function(){
14872         if(!this.proxyDrag){
14873             this.xy = this.el.getXY();
14874             this.adjustAssets();
14875         }
14876     },
14877
14878     // private
14879     adjustAssets : function(doShow){
14880         var x = this.xy[0], y = this.xy[1];
14881         var w = this.size.width, h = this.size.height;
14882         if(doShow === true){
14883             if(this.shadow){
14884                 this.shadow.show(this.el);
14885             }
14886             if(this.shim){
14887                 this.shim.show();
14888             }
14889         }
14890         if(this.shadow && this.shadow.isVisible()){
14891             this.shadow.show(this.el);
14892         }
14893         if(this.shim && this.shim.isVisible()){
14894             this.shim.setBounds(x, y, w, h);
14895         }
14896     },
14897
14898     // private
14899     adjustViewport : function(w, h){
14900         if(!w || !h){
14901             w = Roo.lib.Dom.getViewWidth();
14902             h = Roo.lib.Dom.getViewHeight();
14903         }
14904         // cache the size
14905         this.viewSize = [w, h];
14906         if(this.modal && this.mask.isVisible()){
14907             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14908             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14909         }
14910         if(this.isVisible()){
14911             this.constrainXY();
14912         }
14913     },
14914
14915     /**
14916      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14917      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14918      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14919      */
14920     destroy : function(removeEl){
14921         if(this.isVisible()){
14922             this.animateTarget = null;
14923             this.hide();
14924         }
14925         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14926         if(this.tabs){
14927             this.tabs.destroy(removeEl);
14928         }
14929         Roo.destroy(
14930              this.shim,
14931              this.proxy,
14932              this.resizer,
14933              this.close,
14934              this.mask
14935         );
14936         if(this.dd){
14937             this.dd.unreg();
14938         }
14939         if(this.buttons){
14940            for(var i = 0, len = this.buttons.length; i < len; i++){
14941                this.buttons[i].destroy();
14942            }
14943         }
14944         this.el.removeAllListeners();
14945         if(removeEl === true){
14946             this.el.update("");
14947             this.el.remove();
14948         }
14949         Roo.DialogManager.unregister(this);
14950     },
14951
14952     // private
14953     startMove : function(){
14954         if(this.proxyDrag){
14955             this.proxy.show();
14956         }
14957         if(this.constraintoviewport !== false){
14958             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14959         }
14960     },
14961
14962     // private
14963     endMove : function(){
14964         if(!this.proxyDrag){
14965             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14966         }else{
14967             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14968             this.proxy.hide();
14969         }
14970         this.refreshSize();
14971         this.adjustAssets();
14972         this.focus();
14973         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14974     },
14975
14976     /**
14977      * Brings this dialog to the front of any other visible dialogs
14978      * @return {Roo.BasicDialog} this
14979      */
14980     toFront : function(){
14981         Roo.DialogManager.bringToFront(this);
14982         return this;
14983     },
14984
14985     /**
14986      * Sends this dialog to the back (under) of any other visible dialogs
14987      * @return {Roo.BasicDialog} this
14988      */
14989     toBack : function(){
14990         Roo.DialogManager.sendToBack(this);
14991         return this;
14992     },
14993
14994     /**
14995      * Centers this dialog in the viewport
14996      * @return {Roo.BasicDialog} this
14997      */
14998     center : function(){
14999         var xy = this.el.getCenterXY(true);
15000         this.moveTo(xy[0], xy[1]);
15001         return this;
15002     },
15003
15004     /**
15005      * Moves the dialog's top-left corner to the specified point
15006      * @param {Number} x
15007      * @param {Number} y
15008      * @return {Roo.BasicDialog} this
15009      */
15010     moveTo : function(x, y){
15011         this.xy = [x,y];
15012         if(this.isVisible()){
15013             this.el.setXY(this.xy);
15014             this.adjustAssets();
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Aligns the dialog to the specified element
15021      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15022      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15023      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15024      * @return {Roo.BasicDialog} this
15025      */
15026     alignTo : function(element, position, offsets){
15027         this.xy = this.el.getAlignToXY(element, position, offsets);
15028         if(this.isVisible()){
15029             this.el.setXY(this.xy);
15030             this.adjustAssets();
15031         }
15032         return this;
15033     },
15034
15035     /**
15036      * Anchors an element to another element and realigns it when the window is resized.
15037      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15038      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15039      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15040      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15041      * is a number, it is used as the buffer delay (defaults to 50ms).
15042      * @return {Roo.BasicDialog} this
15043      */
15044     anchorTo : function(el, alignment, offsets, monitorScroll){
15045         var action = function(){
15046             this.alignTo(el, alignment, offsets);
15047         };
15048         Roo.EventManager.onWindowResize(action, this);
15049         var tm = typeof monitorScroll;
15050         if(tm != 'undefined'){
15051             Roo.EventManager.on(window, 'scroll', action, this,
15052                 {buffer: tm == 'number' ? monitorScroll : 50});
15053         }
15054         action.call(this);
15055         return this;
15056     },
15057
15058     /**
15059      * Returns true if the dialog is visible
15060      * @return {Boolean}
15061      */
15062     isVisible : function(){
15063         return this.el.isVisible();
15064     },
15065
15066     // private
15067     animHide : function(callback){
15068         var b = Roo.get(this.animateTarget).getBox();
15069         this.proxy.show();
15070         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15071         this.el.hide();
15072         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15073                     this.hideEl.createDelegate(this, [callback]));
15074     },
15075
15076     /**
15077      * Hides the dialog.
15078      * @param {Function} callback (optional) Function to call when the dialog is hidden
15079      * @return {Roo.BasicDialog} this
15080      */
15081     hide : function(callback){
15082         if (this.fireEvent("beforehide", this) === false){
15083             return;
15084         }
15085         if(this.shadow){
15086             this.shadow.hide();
15087         }
15088         if(this.shim) {
15089           this.shim.hide();
15090         }
15091         // sometimes animateTarget seems to get set.. causing problems...
15092         // this just double checks..
15093         if(this.animateTarget && Roo.get(this.animateTarget)) {
15094            this.animHide(callback);
15095         }else{
15096             this.el.hide();
15097             this.hideEl(callback);
15098         }
15099         return this;
15100     },
15101
15102     // private
15103     hideEl : function(callback){
15104         this.proxy.hide();
15105         if(this.modal){
15106             this.mask.hide();
15107             Roo.get(document.body).removeClass("x-body-masked");
15108         }
15109         this.fireEvent("hide", this);
15110         if(typeof callback == "function"){
15111             callback();
15112         }
15113     },
15114
15115     // private
15116     hideAction : function(){
15117         this.setLeft("-10000px");
15118         this.setTop("-10000px");
15119         this.setStyle("visibility", "hidden");
15120     },
15121
15122     // private
15123     refreshSize : function(){
15124         this.size = this.el.getSize();
15125         this.xy = this.el.getXY();
15126         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15127     },
15128
15129     // private
15130     // z-index is managed by the DialogManager and may be overwritten at any time
15131     setZIndex : function(index){
15132         if(this.modal){
15133             this.mask.setStyle("z-index", index);
15134         }
15135         if(this.shim){
15136             this.shim.setStyle("z-index", ++index);
15137         }
15138         if(this.shadow){
15139             this.shadow.setZIndex(++index);
15140         }
15141         this.el.setStyle("z-index", ++index);
15142         if(this.proxy){
15143             this.proxy.setStyle("z-index", ++index);
15144         }
15145         if(this.resizer){
15146             this.resizer.proxy.setStyle("z-index", ++index);
15147         }
15148
15149         this.lastZIndex = index;
15150     },
15151
15152     /**
15153      * Returns the element for this dialog
15154      * @return {Roo.Element} The underlying dialog Element
15155      */
15156     getEl : function(){
15157         return this.el;
15158     }
15159 });
15160
15161 /**
15162  * @class Roo.DialogManager
15163  * Provides global access to BasicDialogs that have been created and
15164  * support for z-indexing (layering) multiple open dialogs.
15165  */
15166 Roo.DialogManager = function(){
15167     var list = {};
15168     var accessList = [];
15169     var front = null;
15170
15171     // private
15172     var sortDialogs = function(d1, d2){
15173         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15174     };
15175
15176     // private
15177     var orderDialogs = function(){
15178         accessList.sort(sortDialogs);
15179         var seed = Roo.DialogManager.zseed;
15180         for(var i = 0, len = accessList.length; i < len; i++){
15181             var dlg = accessList[i];
15182             if(dlg){
15183                 dlg.setZIndex(seed + (i*10));
15184             }
15185         }
15186     };
15187
15188     return {
15189         /**
15190          * The starting z-index for BasicDialogs (defaults to 9000)
15191          * @type Number The z-index value
15192          */
15193         zseed : 9000,
15194
15195         // private
15196         register : function(dlg){
15197             list[dlg.id] = dlg;
15198             accessList.push(dlg);
15199         },
15200
15201         // private
15202         unregister : function(dlg){
15203             delete list[dlg.id];
15204             var i=0;
15205             var len=0;
15206             if(!accessList.indexOf){
15207                 for(  i = 0, len = accessList.length; i < len; i++){
15208                     if(accessList[i] == dlg){
15209                         accessList.splice(i, 1);
15210                         return;
15211                     }
15212                 }
15213             }else{
15214                  i = accessList.indexOf(dlg);
15215                 if(i != -1){
15216                     accessList.splice(i, 1);
15217                 }
15218             }
15219         },
15220
15221         /**
15222          * Gets a registered dialog by id
15223          * @param {String/Object} id The id of the dialog or a dialog
15224          * @return {Roo.BasicDialog} this
15225          */
15226         get : function(id){
15227             return typeof id == "object" ? id : list[id];
15228         },
15229
15230         /**
15231          * Brings the specified dialog to the front
15232          * @param {String/Object} dlg The id of the dialog or a dialog
15233          * @return {Roo.BasicDialog} this
15234          */
15235         bringToFront : function(dlg){
15236             dlg = this.get(dlg);
15237             if(dlg != front){
15238                 front = dlg;
15239                 dlg._lastAccess = new Date().getTime();
15240                 orderDialogs();
15241             }
15242             return dlg;
15243         },
15244
15245         /**
15246          * Sends the specified dialog to the back
15247          * @param {String/Object} dlg The id of the dialog or a dialog
15248          * @return {Roo.BasicDialog} this
15249          */
15250         sendToBack : function(dlg){
15251             dlg = this.get(dlg);
15252             dlg._lastAccess = -(new Date().getTime());
15253             orderDialogs();
15254             return dlg;
15255         },
15256
15257         /**
15258          * Hides all dialogs
15259          */
15260         hideAll : function(){
15261             for(var id in list){
15262                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15263                     list[id].hide();
15264                 }
15265             }
15266         }
15267     };
15268 }();
15269
15270 /**
15271  * @class Roo.LayoutDialog
15272  * @extends Roo.BasicDialog
15273  * Dialog which provides adjustments for working with a layout in a Dialog.
15274  * Add your necessary layout config options to the dialog's config.<br>
15275  * Example usage (including a nested layout):
15276  * <pre><code>
15277 if(!dialog){
15278     dialog = new Roo.LayoutDialog("download-dlg", {
15279         modal: true,
15280         width:600,
15281         height:450,
15282         shadow:true,
15283         minWidth:500,
15284         minHeight:350,
15285         autoTabs:true,
15286         proxyDrag:true,
15287         // layout config merges with the dialog config
15288         center:{
15289             tabPosition: "top",
15290             alwaysShowTabs: true
15291         }
15292     });
15293     dialog.addKeyListener(27, dialog.hide, dialog);
15294     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15295     dialog.addButton("Build It!", this.getDownload, this);
15296
15297     // we can even add nested layouts
15298     var innerLayout = new Roo.BorderLayout("dl-inner", {
15299         east: {
15300             initialSize: 200,
15301             autoScroll:true,
15302             split:true
15303         },
15304         center: {
15305             autoScroll:true
15306         }
15307     });
15308     innerLayout.beginUpdate();
15309     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15310     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15311     innerLayout.endUpdate(true);
15312
15313     var layout = dialog.getLayout();
15314     layout.beginUpdate();
15315     layout.add("center", new Roo.ContentPanel("standard-panel",
15316                         {title: "Download the Source", fitToFrame:true}));
15317     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15318                {title: "Build your own roo.js"}));
15319     layout.getRegion("center").showPanel(sp);
15320     layout.endUpdate();
15321 }
15322 </code></pre>
15323     * @constructor
15324     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15325     * @param {Object} config configuration options
15326   */
15327 Roo.LayoutDialog = function(el, cfg){
15328     
15329     var config=  cfg;
15330     if (typeof(cfg) == 'undefined') {
15331         config = Roo.apply({}, el);
15332         // not sure why we use documentElement here.. - it should always be body.
15333         // IE7 borks horribly if we use documentElement.
15334         // webkit also does not like documentElement - it creates a body element...
15335         el = Roo.get( document.body || document.documentElement ).createChild();
15336         //config.autoCreate = true;
15337     }
15338     
15339     
15340     config.autoTabs = false;
15341     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15342     this.body.setStyle({overflow:"hidden", position:"relative"});
15343     this.layout = new Roo.BorderLayout(this.body.dom, config);
15344     this.layout.monitorWindowResize = false;
15345     this.el.addClass("x-dlg-auto-layout");
15346     // fix case when center region overwrites center function
15347     this.center = Roo.BasicDialog.prototype.center;
15348     this.on("show", this.layout.layout, this.layout, true);
15349     if (config.items) {
15350         var xitems = config.items;
15351         delete config.items;
15352         Roo.each(xitems, this.addxtype, this);
15353     }
15354     
15355     
15356 };
15357 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15358     /**
15359      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15360      * @deprecated
15361      */
15362     endUpdate : function(){
15363         this.layout.endUpdate();
15364     },
15365
15366     /**
15367      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15368      *  @deprecated
15369      */
15370     beginUpdate : function(){
15371         this.layout.beginUpdate();
15372     },
15373
15374     /**
15375      * Get the BorderLayout for this dialog
15376      * @return {Roo.BorderLayout}
15377      */
15378     getLayout : function(){
15379         return this.layout;
15380     },
15381
15382     showEl : function(){
15383         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15384         if(Roo.isIE7){
15385             this.layout.layout();
15386         }
15387     },
15388
15389     // private
15390     // Use the syncHeightBeforeShow config option to control this automatically
15391     syncBodyHeight : function(){
15392         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15393         if(this.layout){this.layout.layout();}
15394     },
15395     
15396       /**
15397      * Add an xtype element (actually adds to the layout.)
15398      * @return {Object} xdata xtype object data.
15399      */
15400     
15401     addxtype : function(c) {
15402         return this.layout.addxtype(c);
15403     }
15404 });/*
15405  * Based on:
15406  * Ext JS Library 1.1.1
15407  * Copyright(c) 2006-2007, Ext JS, LLC.
15408  *
15409  * Originally Released Under LGPL - original licence link has changed is not relivant.
15410  *
15411  * Fork - LGPL
15412  * <script type="text/javascript">
15413  */
15414  
15415 /**
15416  * @class Roo.MessageBox
15417  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15418  * Example usage:
15419  *<pre><code>
15420 // Basic alert:
15421 Roo.Msg.alert('Status', 'Changes saved successfully.');
15422
15423 // Prompt for user data:
15424 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15425     if (btn == 'ok'){
15426         // process text value...
15427     }
15428 });
15429
15430 // Show a dialog using config options:
15431 Roo.Msg.show({
15432    title:'Save Changes?',
15433    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15434    buttons: Roo.Msg.YESNOCANCEL,
15435    fn: processResult,
15436    animEl: 'elId'
15437 });
15438 </code></pre>
15439  * @singleton
15440  */
15441 Roo.MessageBox = function(){
15442     var dlg, opt, mask, waitTimer;
15443     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15444     var buttons, activeTextEl, bwidth;
15445
15446     // private
15447     var handleButton = function(button){
15448         dlg.hide();
15449         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15450     };
15451
15452     // private
15453     var handleHide = function(){
15454         if(opt && opt.cls){
15455             dlg.el.removeClass(opt.cls);
15456         }
15457         if(waitTimer){
15458             Roo.TaskMgr.stop(waitTimer);
15459             waitTimer = null;
15460         }
15461     };
15462
15463     // private
15464     var updateButtons = function(b){
15465         var width = 0;
15466         if(!b){
15467             buttons["ok"].hide();
15468             buttons["cancel"].hide();
15469             buttons["yes"].hide();
15470             buttons["no"].hide();
15471             dlg.footer.dom.style.display = 'none';
15472             return width;
15473         }
15474         dlg.footer.dom.style.display = '';
15475         for(var k in buttons){
15476             if(typeof buttons[k] != "function"){
15477                 if(b[k]){
15478                     buttons[k].show();
15479                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15480                     width += buttons[k].el.getWidth()+15;
15481                 }else{
15482                     buttons[k].hide();
15483                 }
15484             }
15485         }
15486         return width;
15487     };
15488
15489     // private
15490     var handleEsc = function(d, k, e){
15491         if(opt && opt.closable !== false){
15492             dlg.hide();
15493         }
15494         if(e){
15495             e.stopEvent();
15496         }
15497     };
15498
15499     return {
15500         /**
15501          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15502          * @return {Roo.BasicDialog} The BasicDialog element
15503          */
15504         getDialog : function(){
15505            if(!dlg){
15506                 dlg = new Roo.BasicDialog("x-msg-box", {
15507                     autoCreate : true,
15508                     shadow: true,
15509                     draggable: true,
15510                     resizable:false,
15511                     constraintoviewport:false,
15512                     fixedcenter:true,
15513                     collapsible : false,
15514                     shim:true,
15515                     modal: true,
15516                     width:400, height:100,
15517                     buttonAlign:"center",
15518                     closeClick : function(){
15519                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15520                             handleButton("no");
15521                         }else{
15522                             handleButton("cancel");
15523                         }
15524                     }
15525                 });
15526                 dlg.on("hide", handleHide);
15527                 mask = dlg.mask;
15528                 dlg.addKeyListener(27, handleEsc);
15529                 buttons = {};
15530                 var bt = this.buttonText;
15531                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15532                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15533                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15534                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15535                 bodyEl = dlg.body.createChild({
15536
15537                     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>'
15538                 });
15539                 msgEl = bodyEl.dom.firstChild;
15540                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15541                 textboxEl.enableDisplayMode();
15542                 textboxEl.addKeyListener([10,13], function(){
15543                     if(dlg.isVisible() && opt && opt.buttons){
15544                         if(opt.buttons.ok){
15545                             handleButton("ok");
15546                         }else if(opt.buttons.yes){
15547                             handleButton("yes");
15548                         }
15549                     }
15550                 });
15551                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15552                 textareaEl.enableDisplayMode();
15553                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15554                 progressEl.enableDisplayMode();
15555                 var pf = progressEl.dom.firstChild;
15556                 if (pf) {
15557                     pp = Roo.get(pf.firstChild);
15558                     pp.setHeight(pf.offsetHeight);
15559                 }
15560                 
15561             }
15562             return dlg;
15563         },
15564
15565         /**
15566          * Updates the message box body text
15567          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15568          * the XHTML-compliant non-breaking space character '&amp;#160;')
15569          * @return {Roo.MessageBox} This message box
15570          */
15571         updateText : function(text){
15572             if(!dlg.isVisible() && !opt.width){
15573                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15574             }
15575             msgEl.innerHTML = text || '&#160;';
15576             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15577                         Math.max(opt.minWidth || this.minWidth, bwidth));
15578             if(opt.prompt){
15579                 activeTextEl.setWidth(w);
15580             }
15581             if(dlg.isVisible()){
15582                 dlg.fixedcenter = false;
15583             }
15584             dlg.setContentSize(w, bodyEl.getHeight());
15585             if(dlg.isVisible()){
15586                 dlg.fixedcenter = true;
15587             }
15588             return this;
15589         },
15590
15591         /**
15592          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15593          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15594          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15595          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15596          * @return {Roo.MessageBox} This message box
15597          */
15598         updateProgress : function(value, text){
15599             if(text){
15600                 this.updateText(text);
15601             }
15602             if (pp) { // weird bug on my firefox - for some reason this is not defined
15603                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15604             }
15605             return this;
15606         },        
15607
15608         /**
15609          * Returns true if the message box is currently displayed
15610          * @return {Boolean} True if the message box is visible, else false
15611          */
15612         isVisible : function(){
15613             return dlg && dlg.isVisible();  
15614         },
15615
15616         /**
15617          * Hides the message box if it is displayed
15618          */
15619         hide : function(){
15620             if(this.isVisible()){
15621                 dlg.hide();
15622             }  
15623         },
15624
15625         /**
15626          * Displays a new message box, or reinitializes an existing message box, based on the config options
15627          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15628          * The following config object properties are supported:
15629          * <pre>
15630 Property    Type             Description
15631 ----------  ---------------  ------------------------------------------------------------------------------------
15632 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15633                                    closes (defaults to undefined)
15634 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15635                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15636 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15637                                    progress and wait dialogs will ignore this property and always hide the
15638                                    close button as they can only be closed programmatically.
15639 cls               String           A custom CSS class to apply to the message box element
15640 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15641                                    displayed (defaults to 75)
15642 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15643                                    function will be btn (the name of the button that was clicked, if applicable,
15644                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15645                                    Progress and wait dialogs will ignore this option since they do not respond to
15646                                    user actions and can only be closed programmatically, so any required function
15647                                    should be called by the same code after it closes the dialog.
15648 icon              String           A CSS class that provides a background image to be used as an icon for
15649                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15650 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15651 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15652 modal             Boolean          False to allow user interaction with the page while the message box is
15653                                    displayed (defaults to true)
15654 msg               String           A string that will replace the existing message box body text (defaults
15655                                    to the XHTML-compliant non-breaking space character '&#160;')
15656 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15657 progress          Boolean          True to display a progress bar (defaults to false)
15658 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15659 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15660 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15661 title             String           The title text
15662 value             String           The string value to set into the active textbox element if displayed
15663 wait              Boolean          True to display a progress bar (defaults to false)
15664 width             Number           The width of the dialog in pixels
15665 </pre>
15666          *
15667          * Example usage:
15668          * <pre><code>
15669 Roo.Msg.show({
15670    title: 'Address',
15671    msg: 'Please enter your address:',
15672    width: 300,
15673    buttons: Roo.MessageBox.OKCANCEL,
15674    multiline: true,
15675    fn: saveAddress,
15676    animEl: 'addAddressBtn'
15677 });
15678 </code></pre>
15679          * @param {Object} config Configuration options
15680          * @return {Roo.MessageBox} This message box
15681          */
15682         show : function(options){
15683             if(this.isVisible()){
15684                 this.hide();
15685             }
15686             var d = this.getDialog();
15687             opt = options;
15688             d.setTitle(opt.title || "&#160;");
15689             d.close.setDisplayed(opt.closable !== false);
15690             activeTextEl = textboxEl;
15691             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15692             if(opt.prompt){
15693                 if(opt.multiline){
15694                     textboxEl.hide();
15695                     textareaEl.show();
15696                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15697                         opt.multiline : this.defaultTextHeight);
15698                     activeTextEl = textareaEl;
15699                 }else{
15700                     textboxEl.show();
15701                     textareaEl.hide();
15702                 }
15703             }else{
15704                 textboxEl.hide();
15705                 textareaEl.hide();
15706             }
15707             progressEl.setDisplayed(opt.progress === true);
15708             this.updateProgress(0);
15709             activeTextEl.dom.value = opt.value || "";
15710             if(opt.prompt){
15711                 dlg.setDefaultButton(activeTextEl);
15712             }else{
15713                 var bs = opt.buttons;
15714                 var db = null;
15715                 if(bs && bs.ok){
15716                     db = buttons["ok"];
15717                 }else if(bs && bs.yes){
15718                     db = buttons["yes"];
15719                 }
15720                 dlg.setDefaultButton(db);
15721             }
15722             bwidth = updateButtons(opt.buttons);
15723             this.updateText(opt.msg);
15724             if(opt.cls){
15725                 d.el.addClass(opt.cls);
15726             }
15727             d.proxyDrag = opt.proxyDrag === true;
15728             d.modal = opt.modal !== false;
15729             d.mask = opt.modal !== false ? mask : false;
15730             if(!d.isVisible()){
15731                 // force it to the end of the z-index stack so it gets a cursor in FF
15732                 document.body.appendChild(dlg.el.dom);
15733                 d.animateTarget = null;
15734                 d.show(options.animEl);
15735             }
15736             return this;
15737         },
15738
15739         /**
15740          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15741          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15742          * and closing the message box when the process is complete.
15743          * @param {String} title The title bar text
15744          * @param {String} msg The message box body text
15745          * @return {Roo.MessageBox} This message box
15746          */
15747         progress : function(title, msg){
15748             this.show({
15749                 title : title,
15750                 msg : msg,
15751                 buttons: false,
15752                 progress:true,
15753                 closable:false,
15754                 minWidth: this.minProgressWidth,
15755                 modal : true
15756             });
15757             return this;
15758         },
15759
15760         /**
15761          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15762          * If a callback function is passed it will be called after the user clicks the button, and the
15763          * id of the button that was clicked will be passed as the only parameter to the callback
15764          * (could also be the top-right close button).
15765          * @param {String} title The title bar text
15766          * @param {String} msg The message box body text
15767          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15768          * @param {Object} scope (optional) The scope of the callback function
15769          * @return {Roo.MessageBox} This message box
15770          */
15771         alert : function(title, msg, fn, scope){
15772             this.show({
15773                 title : title,
15774                 msg : msg,
15775                 buttons: this.OK,
15776                 fn: fn,
15777                 scope : scope,
15778                 modal : true
15779             });
15780             return this;
15781         },
15782
15783         /**
15784          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15785          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15786          * You are responsible for closing the message box when the process is complete.
15787          * @param {String} msg The message box body text
15788          * @param {String} title (optional) The title bar text
15789          * @return {Roo.MessageBox} This message box
15790          */
15791         wait : function(msg, title){
15792             this.show({
15793                 title : title,
15794                 msg : msg,
15795                 buttons: false,
15796                 closable:false,
15797                 progress:true,
15798                 modal:true,
15799                 width:300,
15800                 wait:true
15801             });
15802             waitTimer = Roo.TaskMgr.start({
15803                 run: function(i){
15804                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15805                 },
15806                 interval: 1000
15807             });
15808             return this;
15809         },
15810
15811         /**
15812          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15813          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15814          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15815          * @param {String} title The title bar text
15816          * @param {String} msg The message box body text
15817          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15818          * @param {Object} scope (optional) The scope of the callback function
15819          * @return {Roo.MessageBox} This message box
15820          */
15821         confirm : function(title, msg, fn, scope){
15822             this.show({
15823                 title : title,
15824                 msg : msg,
15825                 buttons: this.YESNO,
15826                 fn: fn,
15827                 scope : scope,
15828                 modal : true
15829             });
15830             return this;
15831         },
15832
15833         /**
15834          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15835          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15836          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15837          * (could also be the top-right close button) and the text that was entered will be passed as the two
15838          * parameters to the callback.
15839          * @param {String} title The title bar text
15840          * @param {String} msg The message box body text
15841          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15842          * @param {Object} scope (optional) The scope of the callback function
15843          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15844          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15845          * @return {Roo.MessageBox} This message box
15846          */
15847         prompt : function(title, msg, fn, scope, multiline){
15848             this.show({
15849                 title : title,
15850                 msg : msg,
15851                 buttons: this.OKCANCEL,
15852                 fn: fn,
15853                 minWidth:250,
15854                 scope : scope,
15855                 prompt:true,
15856                 multiline: multiline,
15857                 modal : true
15858             });
15859             return this;
15860         },
15861
15862         /**
15863          * Button config that displays a single OK button
15864          * @type Object
15865          */
15866         OK : {ok:true},
15867         /**
15868          * Button config that displays Yes and No buttons
15869          * @type Object
15870          */
15871         YESNO : {yes:true, no:true},
15872         /**
15873          * Button config that displays OK and Cancel buttons
15874          * @type Object
15875          */
15876         OKCANCEL : {ok:true, cancel:true},
15877         /**
15878          * Button config that displays Yes, No and Cancel buttons
15879          * @type Object
15880          */
15881         YESNOCANCEL : {yes:true, no:true, cancel:true},
15882
15883         /**
15884          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15885          * @type Number
15886          */
15887         defaultTextHeight : 75,
15888         /**
15889          * The maximum width in pixels of the message box (defaults to 600)
15890          * @type Number
15891          */
15892         maxWidth : 600,
15893         /**
15894          * The minimum width in pixels of the message box (defaults to 100)
15895          * @type Number
15896          */
15897         minWidth : 100,
15898         /**
15899          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15900          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15901          * @type Number
15902          */
15903         minProgressWidth : 250,
15904         /**
15905          * An object containing the default button text strings that can be overriden for localized language support.
15906          * Supported properties are: ok, cancel, yes and no.
15907          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15908          * @type Object
15909          */
15910         buttonText : {
15911             ok : "OK",
15912             cancel : "Cancel",
15913             yes : "Yes",
15914             no : "No"
15915         }
15916     };
15917 }();
15918
15919 /**
15920  * Shorthand for {@link Roo.MessageBox}
15921  */
15922 Roo.Msg = Roo.MessageBox;/*
15923  * Based on:
15924  * Ext JS Library 1.1.1
15925  * Copyright(c) 2006-2007, Ext JS, LLC.
15926  *
15927  * Originally Released Under LGPL - original licence link has changed is not relivant.
15928  *
15929  * Fork - LGPL
15930  * <script type="text/javascript">
15931  */
15932 /**
15933  * @class Roo.QuickTips
15934  * Provides attractive and customizable tooltips for any element.
15935  * @singleton
15936  */
15937 Roo.QuickTips = function(){
15938     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15939     var ce, bd, xy, dd;
15940     var visible = false, disabled = true, inited = false;
15941     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15942     
15943     var onOver = function(e){
15944         if(disabled){
15945             return;
15946         }
15947         var t = e.getTarget();
15948         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15949             return;
15950         }
15951         if(ce && t == ce.el){
15952             clearTimeout(hideProc);
15953             return;
15954         }
15955         if(t && tagEls[t.id]){
15956             tagEls[t.id].el = t;
15957             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15958             return;
15959         }
15960         var ttp, et = Roo.fly(t);
15961         var ns = cfg.namespace;
15962         if(tm.interceptTitles && t.title){
15963             ttp = t.title;
15964             t.qtip = ttp;
15965             t.removeAttribute("title");
15966             e.preventDefault();
15967         }else{
15968             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15969         }
15970         if(ttp){
15971             showProc = show.defer(tm.showDelay, tm, [{
15972                 el: t, 
15973                 text: ttp, 
15974                 width: et.getAttributeNS(ns, cfg.width),
15975                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15976                 title: et.getAttributeNS(ns, cfg.title),
15977                     cls: et.getAttributeNS(ns, cfg.cls)
15978             }]);
15979         }
15980     };
15981     
15982     var onOut = function(e){
15983         clearTimeout(showProc);
15984         var t = e.getTarget();
15985         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15986             hideProc = setTimeout(hide, tm.hideDelay);
15987         }
15988     };
15989     
15990     var onMove = function(e){
15991         if(disabled){
15992             return;
15993         }
15994         xy = e.getXY();
15995         xy[1] += 18;
15996         if(tm.trackMouse && ce){
15997             el.setXY(xy);
15998         }
15999     };
16000     
16001     var onDown = function(e){
16002         clearTimeout(showProc);
16003         clearTimeout(hideProc);
16004         if(!e.within(el)){
16005             if(tm.hideOnClick){
16006                 hide();
16007                 tm.disable();
16008                 tm.enable.defer(100, tm);
16009             }
16010         }
16011     };
16012     
16013     var getPad = function(){
16014         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16015     };
16016
16017     var show = function(o){
16018         if(disabled){
16019             return;
16020         }
16021         clearTimeout(dismissProc);
16022         ce = o;
16023         if(removeCls){ // in case manually hidden
16024             el.removeClass(removeCls);
16025             removeCls = null;
16026         }
16027         if(ce.cls){
16028             el.addClass(ce.cls);
16029             removeCls = ce.cls;
16030         }
16031         if(ce.title){
16032             tipTitle.update(ce.title);
16033             tipTitle.show();
16034         }else{
16035             tipTitle.update('');
16036             tipTitle.hide();
16037         }
16038         el.dom.style.width  = tm.maxWidth+'px';
16039         //tipBody.dom.style.width = '';
16040         tipBodyText.update(o.text);
16041         var p = getPad(), w = ce.width;
16042         if(!w){
16043             var td = tipBodyText.dom;
16044             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16045             if(aw > tm.maxWidth){
16046                 w = tm.maxWidth;
16047             }else if(aw < tm.minWidth){
16048                 w = tm.minWidth;
16049             }else{
16050                 w = aw;
16051             }
16052         }
16053         //tipBody.setWidth(w);
16054         el.setWidth(parseInt(w, 10) + p);
16055         if(ce.autoHide === false){
16056             close.setDisplayed(true);
16057             if(dd){
16058                 dd.unlock();
16059             }
16060         }else{
16061             close.setDisplayed(false);
16062             if(dd){
16063                 dd.lock();
16064             }
16065         }
16066         if(xy){
16067             el.avoidY = xy[1]-18;
16068             el.setXY(xy);
16069         }
16070         if(tm.animate){
16071             el.setOpacity(.1);
16072             el.setStyle("visibility", "visible");
16073             el.fadeIn({callback: afterShow});
16074         }else{
16075             afterShow();
16076         }
16077     };
16078     
16079     var afterShow = function(){
16080         if(ce){
16081             el.show();
16082             esc.enable();
16083             if(tm.autoDismiss && ce.autoHide !== false){
16084                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16085             }
16086         }
16087     };
16088     
16089     var hide = function(noanim){
16090         clearTimeout(dismissProc);
16091         clearTimeout(hideProc);
16092         ce = null;
16093         if(el.isVisible()){
16094             esc.disable();
16095             if(noanim !== true && tm.animate){
16096                 el.fadeOut({callback: afterHide});
16097             }else{
16098                 afterHide();
16099             } 
16100         }
16101     };
16102     
16103     var afterHide = function(){
16104         el.hide();
16105         if(removeCls){
16106             el.removeClass(removeCls);
16107             removeCls = null;
16108         }
16109     };
16110     
16111     return {
16112         /**
16113         * @cfg {Number} minWidth
16114         * The minimum width of the quick tip (defaults to 40)
16115         */
16116        minWidth : 40,
16117         /**
16118         * @cfg {Number} maxWidth
16119         * The maximum width of the quick tip (defaults to 300)
16120         */
16121        maxWidth : 300,
16122         /**
16123         * @cfg {Boolean} interceptTitles
16124         * True to automatically use the element's DOM title value if available (defaults to false)
16125         */
16126        interceptTitles : false,
16127         /**
16128         * @cfg {Boolean} trackMouse
16129         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16130         */
16131        trackMouse : false,
16132         /**
16133         * @cfg {Boolean} hideOnClick
16134         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16135         */
16136        hideOnClick : true,
16137         /**
16138         * @cfg {Number} showDelay
16139         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16140         */
16141        showDelay : 500,
16142         /**
16143         * @cfg {Number} hideDelay
16144         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16145         */
16146        hideDelay : 200,
16147         /**
16148         * @cfg {Boolean} autoHide
16149         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16150         * Used in conjunction with hideDelay.
16151         */
16152        autoHide : true,
16153         /**
16154         * @cfg {Boolean}
16155         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16156         * (defaults to true).  Used in conjunction with autoDismissDelay.
16157         */
16158        autoDismiss : true,
16159         /**
16160         * @cfg {Number}
16161         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16162         */
16163        autoDismissDelay : 5000,
16164        /**
16165         * @cfg {Boolean} animate
16166         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16167         */
16168        animate : false,
16169
16170        /**
16171         * @cfg {String} title
16172         * Title text to display (defaults to '').  This can be any valid HTML markup.
16173         */
16174         title: '',
16175        /**
16176         * @cfg {String} text
16177         * Body text to display (defaults to '').  This can be any valid HTML markup.
16178         */
16179         text : '',
16180        /**
16181         * @cfg {String} cls
16182         * A CSS class to apply to the base quick tip element (defaults to '').
16183         */
16184         cls : '',
16185        /**
16186         * @cfg {Number} width
16187         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16188         * minWidth or maxWidth.
16189         */
16190         width : null,
16191
16192     /**
16193      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16194      * or display QuickTips in a page.
16195      */
16196        init : function(){
16197           tm = Roo.QuickTips;
16198           cfg = tm.tagConfig;
16199           if(!inited){
16200               if(!Roo.isReady){ // allow calling of init() before onReady
16201                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16202                   return;
16203               }
16204               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16205               el.fxDefaults = {stopFx: true};
16206               // maximum custom styling
16207               //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>');
16208               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>');              
16209               tipTitle = el.child('h3');
16210               tipTitle.enableDisplayMode("block");
16211               tipBody = el.child('div.x-tip-bd');
16212               tipBodyText = el.child('div.x-tip-bd-inner');
16213               //bdLeft = el.child('div.x-tip-bd-left');
16214               //bdRight = el.child('div.x-tip-bd-right');
16215               close = el.child('div.x-tip-close');
16216               close.enableDisplayMode("block");
16217               close.on("click", hide);
16218               var d = Roo.get(document);
16219               d.on("mousedown", onDown);
16220               d.on("mouseover", onOver);
16221               d.on("mouseout", onOut);
16222               d.on("mousemove", onMove);
16223               esc = d.addKeyListener(27, hide);
16224               esc.disable();
16225               if(Roo.dd.DD){
16226                   dd = el.initDD("default", null, {
16227                       onDrag : function(){
16228                           el.sync();  
16229                       }
16230                   });
16231                   dd.setHandleElId(tipTitle.id);
16232                   dd.lock();
16233               }
16234               inited = true;
16235           }
16236           this.enable(); 
16237        },
16238
16239     /**
16240      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16241      * are supported:
16242      * <pre>
16243 Property    Type                   Description
16244 ----------  ---------------------  ------------------------------------------------------------------------
16245 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16246      * </ul>
16247      * @param {Object} config The config object
16248      */
16249        register : function(config){
16250            var cs = config instanceof Array ? config : arguments;
16251            for(var i = 0, len = cs.length; i < len; i++) {
16252                var c = cs[i];
16253                var target = c.target;
16254                if(target){
16255                    if(target instanceof Array){
16256                        for(var j = 0, jlen = target.length; j < jlen; j++){
16257                            tagEls[target[j]] = c;
16258                        }
16259                    }else{
16260                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16261                    }
16262                }
16263            }
16264        },
16265
16266     /**
16267      * Removes this quick tip from its element and destroys it.
16268      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16269      */
16270        unregister : function(el){
16271            delete tagEls[Roo.id(el)];
16272        },
16273
16274     /**
16275      * Enable this quick tip.
16276      */
16277        enable : function(){
16278            if(inited && disabled){
16279                locks.pop();
16280                if(locks.length < 1){
16281                    disabled = false;
16282                }
16283            }
16284        },
16285
16286     /**
16287      * Disable this quick tip.
16288      */
16289        disable : function(){
16290           disabled = true;
16291           clearTimeout(showProc);
16292           clearTimeout(hideProc);
16293           clearTimeout(dismissProc);
16294           if(ce){
16295               hide(true);
16296           }
16297           locks.push(1);
16298        },
16299
16300     /**
16301      * Returns true if the quick tip is enabled, else false.
16302      */
16303        isEnabled : function(){
16304             return !disabled;
16305        },
16306
16307         // private
16308        tagConfig : {
16309            namespace : "ext",
16310            attribute : "qtip",
16311            width : "width",
16312            target : "target",
16313            title : "qtitle",
16314            hide : "hide",
16315            cls : "qclass"
16316        }
16317    };
16318 }();
16319
16320 // backwards compat
16321 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16322  * Based on:
16323  * Ext JS Library 1.1.1
16324  * Copyright(c) 2006-2007, Ext JS, LLC.
16325  *
16326  * Originally Released Under LGPL - original licence link has changed is not relivant.
16327  *
16328  * Fork - LGPL
16329  * <script type="text/javascript">
16330  */
16331  
16332
16333 /**
16334  * @class Roo.tree.TreePanel
16335  * @extends Roo.data.Tree
16336
16337  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16338  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16339  * @cfg {Boolean} enableDD true to enable drag and drop
16340  * @cfg {Boolean} enableDrag true to enable just drag
16341  * @cfg {Boolean} enableDrop true to enable just drop
16342  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16343  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16344  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16345  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16346  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16347  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16348  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16349  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16350  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16351  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16352  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16353  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16354  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16355  * @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>
16356  * @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>
16357  * 
16358  * @constructor
16359  * @param {String/HTMLElement/Element} el The container element
16360  * @param {Object} config
16361  */
16362 Roo.tree.TreePanel = function(el, config){
16363     var root = false;
16364     var loader = false;
16365     if (config.root) {
16366         root = config.root;
16367         delete config.root;
16368     }
16369     if (config.loader) {
16370         loader = config.loader;
16371         delete config.loader;
16372     }
16373     
16374     Roo.apply(this, config);
16375     Roo.tree.TreePanel.superclass.constructor.call(this);
16376     this.el = Roo.get(el);
16377     this.el.addClass('x-tree');
16378     //console.log(root);
16379     if (root) {
16380         this.setRootNode( Roo.factory(root, Roo.tree));
16381     }
16382     if (loader) {
16383         this.loader = Roo.factory(loader, Roo.tree);
16384     }
16385    /**
16386     * Read-only. The id of the container element becomes this TreePanel's id.
16387     */
16388    this.id = this.el.id;
16389    this.addEvents({
16390         /**
16391         * @event beforeload
16392         * Fires before a node is loaded, return false to cancel
16393         * @param {Node} node The node being loaded
16394         */
16395         "beforeload" : true,
16396         /**
16397         * @event load
16398         * Fires when a node is loaded
16399         * @param {Node} node The node that was loaded
16400         */
16401         "load" : true,
16402         /**
16403         * @event textchange
16404         * Fires when the text for a node is changed
16405         * @param {Node} node The node
16406         * @param {String} text The new text
16407         * @param {String} oldText The old text
16408         */
16409         "textchange" : true,
16410         /**
16411         * @event beforeexpand
16412         * Fires before a node is expanded, return false to cancel.
16413         * @param {Node} node The node
16414         * @param {Boolean} deep
16415         * @param {Boolean} anim
16416         */
16417         "beforeexpand" : true,
16418         /**
16419         * @event beforecollapse
16420         * Fires before a node is collapsed, return false to cancel.
16421         * @param {Node} node The node
16422         * @param {Boolean} deep
16423         * @param {Boolean} anim
16424         */
16425         "beforecollapse" : true,
16426         /**
16427         * @event expand
16428         * Fires when a node is expanded
16429         * @param {Node} node The node
16430         */
16431         "expand" : true,
16432         /**
16433         * @event disabledchange
16434         * Fires when the disabled status of a node changes
16435         * @param {Node} node The node
16436         * @param {Boolean} disabled
16437         */
16438         "disabledchange" : true,
16439         /**
16440         * @event collapse
16441         * Fires when a node is collapsed
16442         * @param {Node} node The node
16443         */
16444         "collapse" : true,
16445         /**
16446         * @event beforeclick
16447         * Fires before click processing on a node. Return false to cancel the default action.
16448         * @param {Node} node The node
16449         * @param {Roo.EventObject} e The event object
16450         */
16451         "beforeclick":true,
16452         /**
16453         * @event checkchange
16454         * Fires when a node with a checkbox's checked property changes
16455         * @param {Node} this This node
16456         * @param {Boolean} checked
16457         */
16458         "checkchange":true,
16459         /**
16460         * @event click
16461         * Fires when a node is clicked
16462         * @param {Node} node The node
16463         * @param {Roo.EventObject} e The event object
16464         */
16465         "click":true,
16466         /**
16467         * @event dblclick
16468         * Fires when a node is double clicked
16469         * @param {Node} node The node
16470         * @param {Roo.EventObject} e The event object
16471         */
16472         "dblclick":true,
16473         /**
16474         * @event contextmenu
16475         * Fires when a node is right clicked
16476         * @param {Node} node The node
16477         * @param {Roo.EventObject} e The event object
16478         */
16479         "contextmenu":true,
16480         /**
16481         * @event beforechildrenrendered
16482         * Fires right before the child nodes for a node are rendered
16483         * @param {Node} node The node
16484         */
16485         "beforechildrenrendered":true,
16486        /**
16487              * @event startdrag
16488              * Fires when a node starts being dragged
16489              * @param {Roo.tree.TreePanel} this
16490              * @param {Roo.tree.TreeNode} node
16491              * @param {event} e The raw browser event
16492              */ 
16493             "startdrag" : true,
16494             /**
16495              * @event enddrag
16496              * Fires when a drag operation is complete
16497              * @param {Roo.tree.TreePanel} this
16498              * @param {Roo.tree.TreeNode} node
16499              * @param {event} e The raw browser event
16500              */
16501             "enddrag" : true,
16502             /**
16503              * @event dragdrop
16504              * Fires when a dragged node is dropped on a valid DD target
16505              * @param {Roo.tree.TreePanel} this
16506              * @param {Roo.tree.TreeNode} node
16507              * @param {DD} dd The dd it was dropped on
16508              * @param {event} e The raw browser event
16509              */
16510             "dragdrop" : true,
16511             /**
16512              * @event beforenodedrop
16513              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16514              * passed to handlers has the following properties:<br />
16515              * <ul style="padding:5px;padding-left:16px;">
16516              * <li>tree - The TreePanel</li>
16517              * <li>target - The node being targeted for the drop</li>
16518              * <li>data - The drag data from the drag source</li>
16519              * <li>point - The point of the drop - append, above or below</li>
16520              * <li>source - The drag source</li>
16521              * <li>rawEvent - Raw mouse event</li>
16522              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16523              * to be inserted by setting them on this object.</li>
16524              * <li>cancel - Set this to true to cancel the drop.</li>
16525              * </ul>
16526              * @param {Object} dropEvent
16527              */
16528             "beforenodedrop" : true,
16529             /**
16530              * @event nodedrop
16531              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16532              * passed to handlers has the following properties:<br />
16533              * <ul style="padding:5px;padding-left:16px;">
16534              * <li>tree - The TreePanel</li>
16535              * <li>target - The node being targeted for the drop</li>
16536              * <li>data - The drag data from the drag source</li>
16537              * <li>point - The point of the drop - append, above or below</li>
16538              * <li>source - The drag source</li>
16539              * <li>rawEvent - Raw mouse event</li>
16540              * <li>dropNode - Dropped node(s).</li>
16541              * </ul>
16542              * @param {Object} dropEvent
16543              */
16544             "nodedrop" : true,
16545              /**
16546              * @event nodedragover
16547              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16548              * passed to handlers has the following properties:<br />
16549              * <ul style="padding:5px;padding-left:16px;">
16550              * <li>tree - The TreePanel</li>
16551              * <li>target - The node being targeted for the drop</li>
16552              * <li>data - The drag data from the drag source</li>
16553              * <li>point - The point of the drop - append, above or below</li>
16554              * <li>source - The drag source</li>
16555              * <li>rawEvent - Raw mouse event</li>
16556              * <li>dropNode - Drop node(s) provided by the source.</li>
16557              * <li>cancel - Set this to true to signal drop not allowed.</li>
16558              * </ul>
16559              * @param {Object} dragOverEvent
16560              */
16561             "nodedragover" : true
16562         
16563    });
16564    if(this.singleExpand){
16565        this.on("beforeexpand", this.restrictExpand, this);
16566    }
16567 };
16568 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16569     rootVisible : true,
16570     animate: Roo.enableFx,
16571     lines : true,
16572     enableDD : false,
16573     hlDrop : Roo.enableFx,
16574   
16575     renderer: false,
16576     
16577     rendererTip: false,
16578     // private
16579     restrictExpand : function(node){
16580         var p = node.parentNode;
16581         if(p){
16582             if(p.expandedChild && p.expandedChild.parentNode == p){
16583                 p.expandedChild.collapse();
16584             }
16585             p.expandedChild = node;
16586         }
16587     },
16588
16589     // private override
16590     setRootNode : function(node){
16591         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16592         if(!this.rootVisible){
16593             node.ui = new Roo.tree.RootTreeNodeUI(node);
16594         }
16595         return node;
16596     },
16597
16598     /**
16599      * Returns the container element for this TreePanel
16600      */
16601     getEl : function(){
16602         return this.el;
16603     },
16604
16605     /**
16606      * Returns the default TreeLoader for this TreePanel
16607      */
16608     getLoader : function(){
16609         return this.loader;
16610     },
16611
16612     /**
16613      * Expand all nodes
16614      */
16615     expandAll : function(){
16616         this.root.expand(true);
16617     },
16618
16619     /**
16620      * Collapse all nodes
16621      */
16622     collapseAll : function(){
16623         this.root.collapse(true);
16624     },
16625
16626     /**
16627      * Returns the selection model used by this TreePanel
16628      */
16629     getSelectionModel : function(){
16630         if(!this.selModel){
16631             this.selModel = new Roo.tree.DefaultSelectionModel();
16632         }
16633         return this.selModel;
16634     },
16635
16636     /**
16637      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16638      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16639      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16640      * @return {Array}
16641      */
16642     getChecked : function(a, startNode){
16643         startNode = startNode || this.root;
16644         var r = [];
16645         var f = function(){
16646             if(this.attributes.checked){
16647                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16648             }
16649         }
16650         startNode.cascade(f);
16651         return r;
16652     },
16653
16654     /**
16655      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16656      * @param {String} path
16657      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16658      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16659      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16660      */
16661     expandPath : function(path, attr, callback){
16662         attr = attr || "id";
16663         var keys = path.split(this.pathSeparator);
16664         var curNode = this.root;
16665         if(curNode.attributes[attr] != keys[1]){ // invalid root
16666             if(callback){
16667                 callback(false, null);
16668             }
16669             return;
16670         }
16671         var index = 1;
16672         var f = function(){
16673             if(++index == keys.length){
16674                 if(callback){
16675                     callback(true, curNode);
16676                 }
16677                 return;
16678             }
16679             var c = curNode.findChild(attr, keys[index]);
16680             if(!c){
16681                 if(callback){
16682                     callback(false, curNode);
16683                 }
16684                 return;
16685             }
16686             curNode = c;
16687             c.expand(false, false, f);
16688         };
16689         curNode.expand(false, false, f);
16690     },
16691
16692     /**
16693      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16694      * @param {String} path
16695      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16696      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16697      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16698      */
16699     selectPath : function(path, attr, callback){
16700         attr = attr || "id";
16701         var keys = path.split(this.pathSeparator);
16702         var v = keys.pop();
16703         if(keys.length > 0){
16704             var f = function(success, node){
16705                 if(success && node){
16706                     var n = node.findChild(attr, v);
16707                     if(n){
16708                         n.select();
16709                         if(callback){
16710                             callback(true, n);
16711                         }
16712                     }else if(callback){
16713                         callback(false, n);
16714                     }
16715                 }else{
16716                     if(callback){
16717                         callback(false, n);
16718                     }
16719                 }
16720             };
16721             this.expandPath(keys.join(this.pathSeparator), attr, f);
16722         }else{
16723             this.root.select();
16724             if(callback){
16725                 callback(true, this.root);
16726             }
16727         }
16728     },
16729
16730     getTreeEl : function(){
16731         return this.el;
16732     },
16733
16734     /**
16735      * Trigger rendering of this TreePanel
16736      */
16737     render : function(){
16738         if (this.innerCt) {
16739             return this; // stop it rendering more than once!!
16740         }
16741         
16742         this.innerCt = this.el.createChild({tag:"ul",
16743                cls:"x-tree-root-ct " +
16744                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16745
16746         if(this.containerScroll){
16747             Roo.dd.ScrollManager.register(this.el);
16748         }
16749         if((this.enableDD || this.enableDrop) && !this.dropZone){
16750            /**
16751             * The dropZone used by this tree if drop is enabled
16752             * @type Roo.tree.TreeDropZone
16753             */
16754              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16755                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16756            });
16757         }
16758         if((this.enableDD || this.enableDrag) && !this.dragZone){
16759            /**
16760             * The dragZone used by this tree if drag is enabled
16761             * @type Roo.tree.TreeDragZone
16762             */
16763             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16764                ddGroup: this.ddGroup || "TreeDD",
16765                scroll: this.ddScroll
16766            });
16767         }
16768         this.getSelectionModel().init(this);
16769         if (!this.root) {
16770             console.log("ROOT not set in tree");
16771             return;
16772         }
16773         this.root.render();
16774         if(!this.rootVisible){
16775             this.root.renderChildren();
16776         }
16777         return this;
16778     }
16779 });/*
16780  * Based on:
16781  * Ext JS Library 1.1.1
16782  * Copyright(c) 2006-2007, Ext JS, LLC.
16783  *
16784  * Originally Released Under LGPL - original licence link has changed is not relivant.
16785  *
16786  * Fork - LGPL
16787  * <script type="text/javascript">
16788  */
16789  
16790
16791 /**
16792  * @class Roo.tree.DefaultSelectionModel
16793  * @extends Roo.util.Observable
16794  * The default single selection for a TreePanel.
16795  */
16796 Roo.tree.DefaultSelectionModel = function(){
16797    this.selNode = null;
16798    
16799    this.addEvents({
16800        /**
16801         * @event selectionchange
16802         * Fires when the selected node changes
16803         * @param {DefaultSelectionModel} this
16804         * @param {TreeNode} node the new selection
16805         */
16806        "selectionchange" : true,
16807
16808        /**
16809         * @event beforeselect
16810         * Fires before the selected node changes, return false to cancel the change
16811         * @param {DefaultSelectionModel} this
16812         * @param {TreeNode} node the new selection
16813         * @param {TreeNode} node the old selection
16814         */
16815        "beforeselect" : true
16816    });
16817 };
16818
16819 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16820     init : function(tree){
16821         this.tree = tree;
16822         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16823         tree.on("click", this.onNodeClick, this);
16824     },
16825     
16826     onNodeClick : function(node, e){
16827         if (e.ctrlKey && this.selNode == node)  {
16828             this.unselect(node);
16829             return;
16830         }
16831         this.select(node);
16832     },
16833     
16834     /**
16835      * Select a node.
16836      * @param {TreeNode} node The node to select
16837      * @return {TreeNode} The selected node
16838      */
16839     select : function(node){
16840         var last = this.selNode;
16841         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16842             if(last){
16843                 last.ui.onSelectedChange(false);
16844             }
16845             this.selNode = node;
16846             node.ui.onSelectedChange(true);
16847             this.fireEvent("selectionchange", this, node, last);
16848         }
16849         return node;
16850     },
16851     
16852     /**
16853      * Deselect a node.
16854      * @param {TreeNode} node The node to unselect
16855      */
16856     unselect : function(node){
16857         if(this.selNode == node){
16858             this.clearSelections();
16859         }    
16860     },
16861     
16862     /**
16863      * Clear all selections
16864      */
16865     clearSelections : function(){
16866         var n = this.selNode;
16867         if(n){
16868             n.ui.onSelectedChange(false);
16869             this.selNode = null;
16870             this.fireEvent("selectionchange", this, null);
16871         }
16872         return n;
16873     },
16874     
16875     /**
16876      * Get the selected node
16877      * @return {TreeNode} The selected node
16878      */
16879     getSelectedNode : function(){
16880         return this.selNode;    
16881     },
16882     
16883     /**
16884      * Returns true if the node is selected
16885      * @param {TreeNode} node The node to check
16886      * @return {Boolean}
16887      */
16888     isSelected : function(node){
16889         return this.selNode == node;  
16890     },
16891
16892     /**
16893      * Selects the node above the selected node in the tree, intelligently walking the nodes
16894      * @return TreeNode The new selection
16895      */
16896     selectPrevious : function(){
16897         var s = this.selNode || this.lastSelNode;
16898         if(!s){
16899             return null;
16900         }
16901         var ps = s.previousSibling;
16902         if(ps){
16903             if(!ps.isExpanded() || ps.childNodes.length < 1){
16904                 return this.select(ps);
16905             } else{
16906                 var lc = ps.lastChild;
16907                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16908                     lc = lc.lastChild;
16909                 }
16910                 return this.select(lc);
16911             }
16912         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16913             return this.select(s.parentNode);
16914         }
16915         return null;
16916     },
16917
16918     /**
16919      * Selects the node above the selected node in the tree, intelligently walking the nodes
16920      * @return TreeNode The new selection
16921      */
16922     selectNext : function(){
16923         var s = this.selNode || this.lastSelNode;
16924         if(!s){
16925             return null;
16926         }
16927         if(s.firstChild && s.isExpanded()){
16928              return this.select(s.firstChild);
16929          }else if(s.nextSibling){
16930              return this.select(s.nextSibling);
16931          }else if(s.parentNode){
16932             var newS = null;
16933             s.parentNode.bubble(function(){
16934                 if(this.nextSibling){
16935                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16936                     return false;
16937                 }
16938             });
16939             return newS;
16940          }
16941         return null;
16942     },
16943
16944     onKeyDown : function(e){
16945         var s = this.selNode || this.lastSelNode;
16946         // undesirable, but required
16947         var sm = this;
16948         if(!s){
16949             return;
16950         }
16951         var k = e.getKey();
16952         switch(k){
16953              case e.DOWN:
16954                  e.stopEvent();
16955                  this.selectNext();
16956              break;
16957              case e.UP:
16958                  e.stopEvent();
16959                  this.selectPrevious();
16960              break;
16961              case e.RIGHT:
16962                  e.preventDefault();
16963                  if(s.hasChildNodes()){
16964                      if(!s.isExpanded()){
16965                          s.expand();
16966                      }else if(s.firstChild){
16967                          this.select(s.firstChild, e);
16968                      }
16969                  }
16970              break;
16971              case e.LEFT:
16972                  e.preventDefault();
16973                  if(s.hasChildNodes() && s.isExpanded()){
16974                      s.collapse();
16975                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16976                      this.select(s.parentNode, e);
16977                  }
16978              break;
16979         };
16980     }
16981 });
16982
16983 /**
16984  * @class Roo.tree.MultiSelectionModel
16985  * @extends Roo.util.Observable
16986  * Multi selection for a TreePanel.
16987  */
16988 Roo.tree.MultiSelectionModel = function(){
16989    this.selNodes = [];
16990    this.selMap = {};
16991    this.addEvents({
16992        /**
16993         * @event selectionchange
16994         * Fires when the selected nodes change
16995         * @param {MultiSelectionModel} this
16996         * @param {Array} nodes Array of the selected nodes
16997         */
16998        "selectionchange" : true
16999    });
17000 };
17001
17002 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17003     init : function(tree){
17004         this.tree = tree;
17005         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17006         tree.on("click", this.onNodeClick, this);
17007     },
17008     
17009     onNodeClick : function(node, e){
17010         this.select(node, e, e.ctrlKey);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @param {EventObject} e (optional) An event associated with the selection
17017      * @param {Boolean} keepExisting True to retain existing selections
17018      * @return {TreeNode} The selected node
17019      */
17020     select : function(node, e, keepExisting){
17021         if(keepExisting !== true){
17022             this.clearSelections(true);
17023         }
17024         if(this.isSelected(node)){
17025             this.lastSelNode = node;
17026             return node;
17027         }
17028         this.selNodes.push(node);
17029         this.selMap[node.id] = node;
17030         this.lastSelNode = node;
17031         node.ui.onSelectedChange(true);
17032         this.fireEvent("selectionchange", this, this.selNodes);
17033         return node;
17034     },
17035     
17036     /**
17037      * Deselect a node.
17038      * @param {TreeNode} node The node to unselect
17039      */
17040     unselect : function(node){
17041         if(this.selMap[node.id]){
17042             node.ui.onSelectedChange(false);
17043             var sn = this.selNodes;
17044             var index = -1;
17045             if(sn.indexOf){
17046                 index = sn.indexOf(node);
17047             }else{
17048                 for(var i = 0, len = sn.length; i < len; i++){
17049                     if(sn[i] == node){
17050                         index = i;
17051                         break;
17052                     }
17053                 }
17054             }
17055             if(index != -1){
17056                 this.selNodes.splice(index, 1);
17057             }
17058             delete this.selMap[node.id];
17059             this.fireEvent("selectionchange", this, this.selNodes);
17060         }
17061     },
17062     
17063     /**
17064      * Clear all selections
17065      */
17066     clearSelections : function(suppressEvent){
17067         var sn = this.selNodes;
17068         if(sn.length > 0){
17069             for(var i = 0, len = sn.length; i < len; i++){
17070                 sn[i].ui.onSelectedChange(false);
17071             }
17072             this.selNodes = [];
17073             this.selMap = {};
17074             if(suppressEvent !== true){
17075                 this.fireEvent("selectionchange", this, this.selNodes);
17076             }
17077         }
17078     },
17079     
17080     /**
17081      * Returns true if the node is selected
17082      * @param {TreeNode} node The node to check
17083      * @return {Boolean}
17084      */
17085     isSelected : function(node){
17086         return this.selMap[node.id] ? true : false;  
17087     },
17088     
17089     /**
17090      * Returns an array of the selected nodes
17091      * @return {Array}
17092      */
17093     getSelectedNodes : function(){
17094         return this.selNodes;    
17095     },
17096
17097     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17098
17099     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17100
17101     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17102 });/*
17103  * Based on:
17104  * Ext JS Library 1.1.1
17105  * Copyright(c) 2006-2007, Ext JS, LLC.
17106  *
17107  * Originally Released Under LGPL - original licence link has changed is not relivant.
17108  *
17109  * Fork - LGPL
17110  * <script type="text/javascript">
17111  */
17112  
17113 /**
17114  * @class Roo.tree.TreeNode
17115  * @extends Roo.data.Node
17116  * @cfg {String} text The text for this node
17117  * @cfg {Boolean} expanded true to start the node expanded
17118  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17119  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17120  * @cfg {Boolean} disabled true to start the node disabled
17121  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17122  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17123  * @cfg {String} cls A css class to be added to the node
17124  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17125  * @cfg {String} href URL of the link used for the node (defaults to #)
17126  * @cfg {String} hrefTarget target frame for the link
17127  * @cfg {String} qtip An Ext QuickTip for the node
17128  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17129  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17130  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17131  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17132  * (defaults to undefined with no checkbox rendered)
17133  * @constructor
17134  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17135  */
17136 Roo.tree.TreeNode = function(attributes){
17137     attributes = attributes || {};
17138     if(typeof attributes == "string"){
17139         attributes = {text: attributes};
17140     }
17141     this.childrenRendered = false;
17142     this.rendered = false;
17143     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17144     this.expanded = attributes.expanded === true;
17145     this.isTarget = attributes.isTarget !== false;
17146     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17147     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17148
17149     /**
17150      * Read-only. The text for this node. To change it use setText().
17151      * @type String
17152      */
17153     this.text = attributes.text;
17154     /**
17155      * True if this node is disabled.
17156      * @type Boolean
17157      */
17158     this.disabled = attributes.disabled === true;
17159
17160     this.addEvents({
17161         /**
17162         * @event textchange
17163         * Fires when the text for this node is changed
17164         * @param {Node} this This node
17165         * @param {String} text The new text
17166         * @param {String} oldText The old text
17167         */
17168         "textchange" : true,
17169         /**
17170         * @event beforeexpand
17171         * Fires before this node is expanded, return false to cancel.
17172         * @param {Node} this This node
17173         * @param {Boolean} deep
17174         * @param {Boolean} anim
17175         */
17176         "beforeexpand" : true,
17177         /**
17178         * @event beforecollapse
17179         * Fires before this node is collapsed, return false to cancel.
17180         * @param {Node} this This node
17181         * @param {Boolean} deep
17182         * @param {Boolean} anim
17183         */
17184         "beforecollapse" : true,
17185         /**
17186         * @event expand
17187         * Fires when this node is expanded
17188         * @param {Node} this This node
17189         */
17190         "expand" : true,
17191         /**
17192         * @event disabledchange
17193         * Fires when the disabled status of this node changes
17194         * @param {Node} this This node
17195         * @param {Boolean} disabled
17196         */
17197         "disabledchange" : true,
17198         /**
17199         * @event collapse
17200         * Fires when this node is collapsed
17201         * @param {Node} this This node
17202         */
17203         "collapse" : true,
17204         /**
17205         * @event beforeclick
17206         * Fires before click processing. Return false to cancel the default action.
17207         * @param {Node} this This node
17208         * @param {Roo.EventObject} e The event object
17209         */
17210         "beforeclick":true,
17211         /**
17212         * @event checkchange
17213         * Fires when a node with a checkbox's checked property changes
17214         * @param {Node} this This node
17215         * @param {Boolean} checked
17216         */
17217         "checkchange":true,
17218         /**
17219         * @event click
17220         * Fires when this node is clicked
17221         * @param {Node} this This node
17222         * @param {Roo.EventObject} e The event object
17223         */
17224         "click":true,
17225         /**
17226         * @event dblclick
17227         * Fires when this node is double clicked
17228         * @param {Node} this This node
17229         * @param {Roo.EventObject} e The event object
17230         */
17231         "dblclick":true,
17232         /**
17233         * @event contextmenu
17234         * Fires when this node is right clicked
17235         * @param {Node} this This node
17236         * @param {Roo.EventObject} e The event object
17237         */
17238         "contextmenu":true,
17239         /**
17240         * @event beforechildrenrendered
17241         * Fires right before the child nodes for this node are rendered
17242         * @param {Node} this This node
17243         */
17244         "beforechildrenrendered":true
17245     });
17246
17247     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17248
17249     /**
17250      * Read-only. The UI for this node
17251      * @type TreeNodeUI
17252      */
17253     this.ui = new uiClass(this);
17254 };
17255 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17256     preventHScroll: true,
17257     /**
17258      * Returns true if this node is expanded
17259      * @return {Boolean}
17260      */
17261     isExpanded : function(){
17262         return this.expanded;
17263     },
17264
17265     /**
17266      * Returns the UI object for this node
17267      * @return {TreeNodeUI}
17268      */
17269     getUI : function(){
17270         return this.ui;
17271     },
17272
17273     // private override
17274     setFirstChild : function(node){
17275         var of = this.firstChild;
17276         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17277         if(this.childrenRendered && of && node != of){
17278             of.renderIndent(true, true);
17279         }
17280         if(this.rendered){
17281             this.renderIndent(true, true);
17282         }
17283     },
17284
17285     // private override
17286     setLastChild : function(node){
17287         var ol = this.lastChild;
17288         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17289         if(this.childrenRendered && ol && node != ol){
17290             ol.renderIndent(true, true);
17291         }
17292         if(this.rendered){
17293             this.renderIndent(true, true);
17294         }
17295     },
17296
17297     // these methods are overridden to provide lazy rendering support
17298     // private override
17299     appendChild : function(){
17300         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17301         if(node && this.childrenRendered){
17302             node.render();
17303         }
17304         this.ui.updateExpandIcon();
17305         return node;
17306     },
17307
17308     // private override
17309     removeChild : function(node){
17310         this.ownerTree.getSelectionModel().unselect(node);
17311         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17312         // if it's been rendered remove dom node
17313         if(this.childrenRendered){
17314             node.ui.remove();
17315         }
17316         if(this.childNodes.length < 1){
17317             this.collapse(false, false);
17318         }else{
17319             this.ui.updateExpandIcon();
17320         }
17321         if(!this.firstChild) {
17322             this.childrenRendered = false;
17323         }
17324         return node;
17325     },
17326
17327     // private override
17328     insertBefore : function(node, refNode){
17329         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17330         if(newNode && refNode && this.childrenRendered){
17331             node.render();
17332         }
17333         this.ui.updateExpandIcon();
17334         return newNode;
17335     },
17336
17337     /**
17338      * Sets the text for this node
17339      * @param {String} text
17340      */
17341     setText : function(text){
17342         var oldText = this.text;
17343         this.text = text;
17344         this.attributes.text = text;
17345         if(this.rendered){ // event without subscribing
17346             this.ui.onTextChange(this, text, oldText);
17347         }
17348         this.fireEvent("textchange", this, text, oldText);
17349     },
17350
17351     /**
17352      * Triggers selection of this node
17353      */
17354     select : function(){
17355         this.getOwnerTree().getSelectionModel().select(this);
17356     },
17357
17358     /**
17359      * Triggers deselection of this node
17360      */
17361     unselect : function(){
17362         this.getOwnerTree().getSelectionModel().unselect(this);
17363     },
17364
17365     /**
17366      * Returns true if this node is selected
17367      * @return {Boolean}
17368      */
17369     isSelected : function(){
17370         return this.getOwnerTree().getSelectionModel().isSelected(this);
17371     },
17372
17373     /**
17374      * Expand this node.
17375      * @param {Boolean} deep (optional) True to expand all children as well
17376      * @param {Boolean} anim (optional) false to cancel the default animation
17377      * @param {Function} callback (optional) A callback to be called when
17378      * expanding this node completes (does not wait for deep expand to complete).
17379      * Called with 1 parameter, this node.
17380      */
17381     expand : function(deep, anim, callback){
17382         if(!this.expanded){
17383             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17384                 return;
17385             }
17386             if(!this.childrenRendered){
17387                 this.renderChildren();
17388             }
17389             this.expanded = true;
17390             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17391                 this.ui.animExpand(function(){
17392                     this.fireEvent("expand", this);
17393                     if(typeof callback == "function"){
17394                         callback(this);
17395                     }
17396                     if(deep === true){
17397                         this.expandChildNodes(true);
17398                     }
17399                 }.createDelegate(this));
17400                 return;
17401             }else{
17402                 this.ui.expand();
17403                 this.fireEvent("expand", this);
17404                 if(typeof callback == "function"){
17405                     callback(this);
17406                 }
17407             }
17408         }else{
17409            if(typeof callback == "function"){
17410                callback(this);
17411            }
17412         }
17413         if(deep === true){
17414             this.expandChildNodes(true);
17415         }
17416     },
17417
17418     isHiddenRoot : function(){
17419         return this.isRoot && !this.getOwnerTree().rootVisible;
17420     },
17421
17422     /**
17423      * Collapse this node.
17424      * @param {Boolean} deep (optional) True to collapse all children as well
17425      * @param {Boolean} anim (optional) false to cancel the default animation
17426      */
17427     collapse : function(deep, anim){
17428         if(this.expanded && !this.isHiddenRoot()){
17429             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17430                 return;
17431             }
17432             this.expanded = false;
17433             if((this.getOwnerTree().animate && anim !== false) || anim){
17434                 this.ui.animCollapse(function(){
17435                     this.fireEvent("collapse", this);
17436                     if(deep === true){
17437                         this.collapseChildNodes(true);
17438                     }
17439                 }.createDelegate(this));
17440                 return;
17441             }else{
17442                 this.ui.collapse();
17443                 this.fireEvent("collapse", this);
17444             }
17445         }
17446         if(deep === true){
17447             var cs = this.childNodes;
17448             for(var i = 0, len = cs.length; i < len; i++) {
17449                 cs[i].collapse(true, false);
17450             }
17451         }
17452     },
17453
17454     // private
17455     delayedExpand : function(delay){
17456         if(!this.expandProcId){
17457             this.expandProcId = this.expand.defer(delay, this);
17458         }
17459     },
17460
17461     // private
17462     cancelExpand : function(){
17463         if(this.expandProcId){
17464             clearTimeout(this.expandProcId);
17465         }
17466         this.expandProcId = false;
17467     },
17468
17469     /**
17470      * Toggles expanded/collapsed state of the node
17471      */
17472     toggle : function(){
17473         if(this.expanded){
17474             this.collapse();
17475         }else{
17476             this.expand();
17477         }
17478     },
17479
17480     /**
17481      * Ensures all parent nodes are expanded
17482      */
17483     ensureVisible : function(callback){
17484         var tree = this.getOwnerTree();
17485         tree.expandPath(this.parentNode.getPath(), false, function(){
17486             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17487             Roo.callback(callback);
17488         }.createDelegate(this));
17489     },
17490
17491     /**
17492      * Expand all child nodes
17493      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17494      */
17495     expandChildNodes : function(deep){
17496         var cs = this.childNodes;
17497         for(var i = 0, len = cs.length; i < len; i++) {
17498                 cs[i].expand(deep);
17499         }
17500     },
17501
17502     /**
17503      * Collapse all child nodes
17504      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17505      */
17506     collapseChildNodes : function(deep){
17507         var cs = this.childNodes;
17508         for(var i = 0, len = cs.length; i < len; i++) {
17509                 cs[i].collapse(deep);
17510         }
17511     },
17512
17513     /**
17514      * Disables this node
17515      */
17516     disable : function(){
17517         this.disabled = true;
17518         this.unselect();
17519         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17520             this.ui.onDisableChange(this, true);
17521         }
17522         this.fireEvent("disabledchange", this, true);
17523     },
17524
17525     /**
17526      * Enables this node
17527      */
17528     enable : function(){
17529         this.disabled = false;
17530         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17531             this.ui.onDisableChange(this, false);
17532         }
17533         this.fireEvent("disabledchange", this, false);
17534     },
17535
17536     // private
17537     renderChildren : function(suppressEvent){
17538         if(suppressEvent !== false){
17539             this.fireEvent("beforechildrenrendered", this);
17540         }
17541         var cs = this.childNodes;
17542         for(var i = 0, len = cs.length; i < len; i++){
17543             cs[i].render(true);
17544         }
17545         this.childrenRendered = true;
17546     },
17547
17548     // private
17549     sort : function(fn, scope){
17550         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17551         if(this.childrenRendered){
17552             var cs = this.childNodes;
17553             for(var i = 0, len = cs.length; i < len; i++){
17554                 cs[i].render(true);
17555             }
17556         }
17557     },
17558
17559     // private
17560     render : function(bulkRender){
17561         this.ui.render(bulkRender);
17562         if(!this.rendered){
17563             this.rendered = true;
17564             if(this.expanded){
17565                 this.expanded = false;
17566                 this.expand(false, false);
17567             }
17568         }
17569     },
17570
17571     // private
17572     renderIndent : function(deep, refresh){
17573         if(refresh){
17574             this.ui.childIndent = null;
17575         }
17576         this.ui.renderIndent();
17577         if(deep === true && this.childrenRendered){
17578             var cs = this.childNodes;
17579             for(var i = 0, len = cs.length; i < len; i++){
17580                 cs[i].renderIndent(true, refresh);
17581             }
17582         }
17583     }
17584 });/*
17585  * Based on:
17586  * Ext JS Library 1.1.1
17587  * Copyright(c) 2006-2007, Ext JS, LLC.
17588  *
17589  * Originally Released Under LGPL - original licence link has changed is not relivant.
17590  *
17591  * Fork - LGPL
17592  * <script type="text/javascript">
17593  */
17594  
17595 /**
17596  * @class Roo.tree.AsyncTreeNode
17597  * @extends Roo.tree.TreeNode
17598  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17599  * @constructor
17600  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17601  */
17602  Roo.tree.AsyncTreeNode = function(config){
17603     this.loaded = false;
17604     this.loading = false;
17605     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17606     /**
17607     * @event beforeload
17608     * Fires before this node is loaded, return false to cancel
17609     * @param {Node} this This node
17610     */
17611     this.addEvents({'beforeload':true, 'load': true});
17612     /**
17613     * @event load
17614     * Fires when this node is loaded
17615     * @param {Node} this This node
17616     */
17617     /**
17618      * The loader used by this node (defaults to using the tree's defined loader)
17619      * @type TreeLoader
17620      * @property loader
17621      */
17622 };
17623 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17624     expand : function(deep, anim, callback){
17625         if(this.loading){ // if an async load is already running, waiting til it's done
17626             var timer;
17627             var f = function(){
17628                 if(!this.loading){ // done loading
17629                     clearInterval(timer);
17630                     this.expand(deep, anim, callback);
17631                 }
17632             }.createDelegate(this);
17633             timer = setInterval(f, 200);
17634             return;
17635         }
17636         if(!this.loaded){
17637             if(this.fireEvent("beforeload", this) === false){
17638                 return;
17639             }
17640             this.loading = true;
17641             this.ui.beforeLoad(this);
17642             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17643             if(loader){
17644                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17645                 return;
17646             }
17647         }
17648         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17649     },
17650     
17651     /**
17652      * Returns true if this node is currently loading
17653      * @return {Boolean}
17654      */
17655     isLoading : function(){
17656         return this.loading;  
17657     },
17658     
17659     loadComplete : function(deep, anim, callback){
17660         this.loading = false;
17661         this.loaded = true;
17662         this.ui.afterLoad(this);
17663         this.fireEvent("load", this);
17664         this.expand(deep, anim, callback);
17665     },
17666     
17667     /**
17668      * Returns true if this node has been loaded
17669      * @return {Boolean}
17670      */
17671     isLoaded : function(){
17672         return this.loaded;
17673     },
17674     
17675     hasChildNodes : function(){
17676         if(!this.isLeaf() && !this.loaded){
17677             return true;
17678         }else{
17679             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17680         }
17681     },
17682
17683     /**
17684      * Trigger a reload for this node
17685      * @param {Function} callback
17686      */
17687     reload : function(callback){
17688         this.collapse(false, false);
17689         while(this.firstChild){
17690             this.removeChild(this.firstChild);
17691         }
17692         this.childrenRendered = false;
17693         this.loaded = false;
17694         if(this.isHiddenRoot()){
17695             this.expanded = false;
17696         }
17697         this.expand(false, false, callback);
17698     }
17699 });/*
17700  * Based on:
17701  * Ext JS Library 1.1.1
17702  * Copyright(c) 2006-2007, Ext JS, LLC.
17703  *
17704  * Originally Released Under LGPL - original licence link has changed is not relivant.
17705  *
17706  * Fork - LGPL
17707  * <script type="text/javascript">
17708  */
17709  
17710 /**
17711  * @class Roo.tree.TreeNodeUI
17712  * @constructor
17713  * @param {Object} node The node to render
17714  * The TreeNode UI implementation is separate from the
17715  * tree implementation. Unless you are customizing the tree UI,
17716  * you should never have to use this directly.
17717  */
17718 Roo.tree.TreeNodeUI = function(node){
17719     this.node = node;
17720     this.rendered = false;
17721     this.animating = false;
17722     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17723 };
17724
17725 Roo.tree.TreeNodeUI.prototype = {
17726     removeChild : function(node){
17727         if(this.rendered){
17728             this.ctNode.removeChild(node.ui.getEl());
17729         }
17730     },
17731
17732     beforeLoad : function(){
17733          this.addClass("x-tree-node-loading");
17734     },
17735
17736     afterLoad : function(){
17737          this.removeClass("x-tree-node-loading");
17738     },
17739
17740     onTextChange : function(node, text, oldText){
17741         if(this.rendered){
17742             this.textNode.innerHTML = text;
17743         }
17744     },
17745
17746     onDisableChange : function(node, state){
17747         this.disabled = state;
17748         if(state){
17749             this.addClass("x-tree-node-disabled");
17750         }else{
17751             this.removeClass("x-tree-node-disabled");
17752         }
17753     },
17754
17755     onSelectedChange : function(state){
17756         if(state){
17757             this.focus();
17758             this.addClass("x-tree-selected");
17759         }else{
17760             //this.blur();
17761             this.removeClass("x-tree-selected");
17762         }
17763     },
17764
17765     onMove : function(tree, node, oldParent, newParent, index, refNode){
17766         this.childIndent = null;
17767         if(this.rendered){
17768             var targetNode = newParent.ui.getContainer();
17769             if(!targetNode){//target not rendered
17770                 this.holder = document.createElement("div");
17771                 this.holder.appendChild(this.wrap);
17772                 return;
17773             }
17774             var insertBefore = refNode ? refNode.ui.getEl() : null;
17775             if(insertBefore){
17776                 targetNode.insertBefore(this.wrap, insertBefore);
17777             }else{
17778                 targetNode.appendChild(this.wrap);
17779             }
17780             this.node.renderIndent(true);
17781         }
17782     },
17783
17784     addClass : function(cls){
17785         if(this.elNode){
17786             Roo.fly(this.elNode).addClass(cls);
17787         }
17788     },
17789
17790     removeClass : function(cls){
17791         if(this.elNode){
17792             Roo.fly(this.elNode).removeClass(cls);
17793         }
17794     },
17795
17796     remove : function(){
17797         if(this.rendered){
17798             this.holder = document.createElement("div");
17799             this.holder.appendChild(this.wrap);
17800         }
17801     },
17802
17803     fireEvent : function(){
17804         return this.node.fireEvent.apply(this.node, arguments);
17805     },
17806
17807     initEvents : function(){
17808         this.node.on("move", this.onMove, this);
17809         var E = Roo.EventManager;
17810         var a = this.anchor;
17811
17812         var el = Roo.fly(a, '_treeui');
17813
17814         if(Roo.isOpera){ // opera render bug ignores the CSS
17815             el.setStyle("text-decoration", "none");
17816         }
17817
17818         el.on("click", this.onClick, this);
17819         el.on("dblclick", this.onDblClick, this);
17820
17821         if(this.checkbox){
17822             Roo.EventManager.on(this.checkbox,
17823                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17824         }
17825
17826         el.on("contextmenu", this.onContextMenu, this);
17827
17828         var icon = Roo.fly(this.iconNode);
17829         icon.on("click", this.onClick, this);
17830         icon.on("dblclick", this.onDblClick, this);
17831         icon.on("contextmenu", this.onContextMenu, this);
17832         E.on(this.ecNode, "click", this.ecClick, this, true);
17833
17834         if(this.node.disabled){
17835             this.addClass("x-tree-node-disabled");
17836         }
17837         if(this.node.hidden){
17838             this.addClass("x-tree-node-disabled");
17839         }
17840         var ot = this.node.getOwnerTree();
17841         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17842         if(dd && (!this.node.isRoot || ot.rootVisible)){
17843             Roo.dd.Registry.register(this.elNode, {
17844                 node: this.node,
17845                 handles: this.getDDHandles(),
17846                 isHandle: false
17847             });
17848         }
17849     },
17850
17851     getDDHandles : function(){
17852         return [this.iconNode, this.textNode];
17853     },
17854
17855     hide : function(){
17856         if(this.rendered){
17857             this.wrap.style.display = "none";
17858         }
17859     },
17860
17861     show : function(){
17862         if(this.rendered){
17863             this.wrap.style.display = "";
17864         }
17865     },
17866
17867     onContextMenu : function(e){
17868         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17869             e.preventDefault();
17870             this.focus();
17871             this.fireEvent("contextmenu", this.node, e);
17872         }
17873     },
17874
17875     onClick : function(e){
17876         if(this.dropping){
17877             e.stopEvent();
17878             return;
17879         }
17880         if(this.fireEvent("beforeclick", this.node, e) !== false){
17881             if(!this.disabled && this.node.attributes.href){
17882                 this.fireEvent("click", this.node, e);
17883                 return;
17884             }
17885             e.preventDefault();
17886             if(this.disabled){
17887                 return;
17888             }
17889
17890             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17891                 this.node.toggle();
17892             }
17893
17894             this.fireEvent("click", this.node, e);
17895         }else{
17896             e.stopEvent();
17897         }
17898     },
17899
17900     onDblClick : function(e){
17901         e.preventDefault();
17902         if(this.disabled){
17903             return;
17904         }
17905         if(this.checkbox){
17906             this.toggleCheck();
17907         }
17908         if(!this.animating && this.node.hasChildNodes()){
17909             this.node.toggle();
17910         }
17911         this.fireEvent("dblclick", this.node, e);
17912     },
17913
17914     onCheckChange : function(){
17915         var checked = this.checkbox.checked;
17916         this.node.attributes.checked = checked;
17917         this.fireEvent('checkchange', this.node, checked);
17918     },
17919
17920     ecClick : function(e){
17921         if(!this.animating && this.node.hasChildNodes()){
17922             this.node.toggle();
17923         }
17924     },
17925
17926     startDrop : function(){
17927         this.dropping = true;
17928     },
17929
17930     // delayed drop so the click event doesn't get fired on a drop
17931     endDrop : function(){
17932        setTimeout(function(){
17933            this.dropping = false;
17934        }.createDelegate(this), 50);
17935     },
17936
17937     expand : function(){
17938         this.updateExpandIcon();
17939         this.ctNode.style.display = "";
17940     },
17941
17942     focus : function(){
17943         if(!this.node.preventHScroll){
17944             try{this.anchor.focus();
17945             }catch(e){}
17946         }else if(!Roo.isIE){
17947             try{
17948                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17949                 var l = noscroll.scrollLeft;
17950                 this.anchor.focus();
17951                 noscroll.scrollLeft = l;
17952             }catch(e){}
17953         }
17954     },
17955
17956     toggleCheck : function(value){
17957         var cb = this.checkbox;
17958         if(cb){
17959             cb.checked = (value === undefined ? !cb.checked : value);
17960         }
17961     },
17962
17963     blur : function(){
17964         try{
17965             this.anchor.blur();
17966         }catch(e){}
17967     },
17968
17969     animExpand : function(callback){
17970         var ct = Roo.get(this.ctNode);
17971         ct.stopFx();
17972         if(!this.node.hasChildNodes()){
17973             this.updateExpandIcon();
17974             this.ctNode.style.display = "";
17975             Roo.callback(callback);
17976             return;
17977         }
17978         this.animating = true;
17979         this.updateExpandIcon();
17980
17981         ct.slideIn('t', {
17982            callback : function(){
17983                this.animating = false;
17984                Roo.callback(callback);
17985             },
17986             scope: this,
17987             duration: this.node.ownerTree.duration || .25
17988         });
17989     },
17990
17991     highlight : function(){
17992         var tree = this.node.getOwnerTree();
17993         Roo.fly(this.wrap).highlight(
17994             tree.hlColor || "C3DAF9",
17995             {endColor: tree.hlBaseColor}
17996         );
17997     },
17998
17999     collapse : function(){
18000         this.updateExpandIcon();
18001         this.ctNode.style.display = "none";
18002     },
18003
18004     animCollapse : function(callback){
18005         var ct = Roo.get(this.ctNode);
18006         ct.enableDisplayMode('block');
18007         ct.stopFx();
18008
18009         this.animating = true;
18010         this.updateExpandIcon();
18011
18012         ct.slideOut('t', {
18013             callback : function(){
18014                this.animating = false;
18015                Roo.callback(callback);
18016             },
18017             scope: this,
18018             duration: this.node.ownerTree.duration || .25
18019         });
18020     },
18021
18022     getContainer : function(){
18023         return this.ctNode;
18024     },
18025
18026     getEl : function(){
18027         return this.wrap;
18028     },
18029
18030     appendDDGhost : function(ghostNode){
18031         ghostNode.appendChild(this.elNode.cloneNode(true));
18032     },
18033
18034     getDDRepairXY : function(){
18035         return Roo.lib.Dom.getXY(this.iconNode);
18036     },
18037
18038     onRender : function(){
18039         this.render();
18040     },
18041
18042     render : function(bulkRender){
18043         var n = this.node, a = n.attributes;
18044         var targetNode = n.parentNode ?
18045               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18046
18047         if(!this.rendered){
18048             this.rendered = true;
18049
18050             this.renderElements(n, a, targetNode, bulkRender);
18051
18052             if(a.qtip){
18053                if(this.textNode.setAttributeNS){
18054                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18055                    if(a.qtipTitle){
18056                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18057                    }
18058                }else{
18059                    this.textNode.setAttribute("ext:qtip", a.qtip);
18060                    if(a.qtipTitle){
18061                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18062                    }
18063                }
18064             }else if(a.qtipCfg){
18065                 a.qtipCfg.target = Roo.id(this.textNode);
18066                 Roo.QuickTips.register(a.qtipCfg);
18067             }
18068             this.initEvents();
18069             if(!this.node.expanded){
18070                 this.updateExpandIcon();
18071             }
18072         }else{
18073             if(bulkRender === true) {
18074                 targetNode.appendChild(this.wrap);
18075             }
18076         }
18077     },
18078
18079     renderElements : function(n, a, targetNode, bulkRender){
18080         // add some indent caching, this helps performance when rendering a large tree
18081         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18082         var t = n.getOwnerTree();
18083         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18084         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18085         var cb = typeof a.checked == 'boolean';
18086         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18087         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18088             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18089             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18090             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18091             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18092             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18093              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18094                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18095             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18096             "</li>"];
18097
18098         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18099             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18100                                 n.nextSibling.ui.getEl(), buf.join(""));
18101         }else{
18102             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18103         }
18104
18105         this.elNode = this.wrap.childNodes[0];
18106         this.ctNode = this.wrap.childNodes[1];
18107         var cs = this.elNode.childNodes;
18108         this.indentNode = cs[0];
18109         this.ecNode = cs[1];
18110         this.iconNode = cs[2];
18111         var index = 3;
18112         if(cb){
18113             this.checkbox = cs[3];
18114             index++;
18115         }
18116         this.anchor = cs[index];
18117         this.textNode = cs[index].firstChild;
18118     },
18119
18120     getAnchor : function(){
18121         return this.anchor;
18122     },
18123
18124     getTextEl : function(){
18125         return this.textNode;
18126     },
18127
18128     getIconEl : function(){
18129         return this.iconNode;
18130     },
18131
18132     isChecked : function(){
18133         return this.checkbox ? this.checkbox.checked : false;
18134     },
18135
18136     updateExpandIcon : function(){
18137         if(this.rendered){
18138             var n = this.node, c1, c2;
18139             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18140             var hasChild = n.hasChildNodes();
18141             if(hasChild){
18142                 if(n.expanded){
18143                     cls += "-minus";
18144                     c1 = "x-tree-node-collapsed";
18145                     c2 = "x-tree-node-expanded";
18146                 }else{
18147                     cls += "-plus";
18148                     c1 = "x-tree-node-expanded";
18149                     c2 = "x-tree-node-collapsed";
18150                 }
18151                 if(this.wasLeaf){
18152                     this.removeClass("x-tree-node-leaf");
18153                     this.wasLeaf = false;
18154                 }
18155                 if(this.c1 != c1 || this.c2 != c2){
18156                     Roo.fly(this.elNode).replaceClass(c1, c2);
18157                     this.c1 = c1; this.c2 = c2;
18158                 }
18159             }else{
18160                 if(!this.wasLeaf){
18161                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18162                     delete this.c1;
18163                     delete this.c2;
18164                     this.wasLeaf = true;
18165                 }
18166             }
18167             var ecc = "x-tree-ec-icon "+cls;
18168             if(this.ecc != ecc){
18169                 this.ecNode.className = ecc;
18170                 this.ecc = ecc;
18171             }
18172         }
18173     },
18174
18175     getChildIndent : function(){
18176         if(!this.childIndent){
18177             var buf = [];
18178             var p = this.node;
18179             while(p){
18180                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18181                     if(!p.isLast()) {
18182                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18183                     } else {
18184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18185                     }
18186                 }
18187                 p = p.parentNode;
18188             }
18189             this.childIndent = buf.join("");
18190         }
18191         return this.childIndent;
18192     },
18193
18194     renderIndent : function(){
18195         if(this.rendered){
18196             var indent = "";
18197             var p = this.node.parentNode;
18198             if(p){
18199                 indent = p.ui.getChildIndent();
18200             }
18201             if(this.indentMarkup != indent){ // don't rerender if not required
18202                 this.indentNode.innerHTML = indent;
18203                 this.indentMarkup = indent;
18204             }
18205             this.updateExpandIcon();
18206         }
18207     }
18208 };
18209
18210 Roo.tree.RootTreeNodeUI = function(){
18211     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18212 };
18213 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18214     render : function(){
18215         if(!this.rendered){
18216             var targetNode = this.node.ownerTree.innerCt.dom;
18217             this.node.expanded = true;
18218             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18219             this.wrap = this.ctNode = targetNode.firstChild;
18220         }
18221     },
18222     collapse : function(){
18223     },
18224     expand : function(){
18225     }
18226 });/*
18227  * Based on:
18228  * Ext JS Library 1.1.1
18229  * Copyright(c) 2006-2007, Ext JS, LLC.
18230  *
18231  * Originally Released Under LGPL - original licence link has changed is not relivant.
18232  *
18233  * Fork - LGPL
18234  * <script type="text/javascript">
18235  */
18236 /**
18237  * @class Roo.tree.TreeLoader
18238  * @extends Roo.util.Observable
18239  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18240  * nodes from a specified URL. The response must be a javascript Array definition
18241  * who's elements are node definition objects. eg:
18242  * <pre><code>
18243    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18244     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18245 </code></pre>
18246  * <br><br>
18247  * A server request is sent, and child nodes are loaded only when a node is expanded.
18248  * The loading node's id is passed to the server under the parameter name "node" to
18249  * enable the server to produce the correct child nodes.
18250  * <br><br>
18251  * To pass extra parameters, an event handler may be attached to the "beforeload"
18252  * event, and the parameters specified in the TreeLoader's baseParams property:
18253  * <pre><code>
18254     myTreeLoader.on("beforeload", function(treeLoader, node) {
18255         this.baseParams.category = node.attributes.category;
18256     }, this);
18257 </code></pre><
18258  * This would pass an HTTP parameter called "category" to the server containing
18259  * the value of the Node's "category" attribute.
18260  * @constructor
18261  * Creates a new Treeloader.
18262  * @param {Object} config A config object containing config properties.
18263  */
18264 Roo.tree.TreeLoader = function(config){
18265     this.baseParams = {};
18266     this.requestMethod = "POST";
18267     Roo.apply(this, config);
18268
18269     this.addEvents({
18270     
18271         /**
18272          * @event beforeload
18273          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18274          * @param {Object} This TreeLoader object.
18275          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18276          * @param {Object} callback The callback function specified in the {@link #load} call.
18277          */
18278         beforeload : true,
18279         /**
18280          * @event load
18281          * Fires when the node has been successfuly loaded.
18282          * @param {Object} This TreeLoader object.
18283          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18284          * @param {Object} response The response object containing the data from the server.
18285          */
18286         load : true,
18287         /**
18288          * @event loadexception
18289          * Fires if the network request failed.
18290          * @param {Object} This TreeLoader object.
18291          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18292          * @param {Object} response The response object containing the data from the server.
18293          */
18294         loadexception : true,
18295         /**
18296          * @event create
18297          * Fires before a node is created, enabling you to return custom Node types 
18298          * @param {Object} This TreeLoader object.
18299          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18300          */
18301         create : true
18302     });
18303
18304     Roo.tree.TreeLoader.superclass.constructor.call(this);
18305 };
18306
18307 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18308     /**
18309     * @cfg {String} dataUrl The URL from which to request a Json string which
18310     * specifies an array of node definition object representing the child nodes
18311     * to be loaded.
18312     */
18313     /**
18314     * @cfg {Object} baseParams (optional) An object containing properties which
18315     * specify HTTP parameters to be passed to each request for child nodes.
18316     */
18317     /**
18318     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18319     * created by this loader. If the attributes sent by the server have an attribute in this object,
18320     * they take priority.
18321     */
18322     /**
18323     * @cfg {Object} uiProviders (optional) An object containing properties which
18324     * 
18325     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18326     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18327     * <i>uiProvider</i> attribute of a returned child node is a string rather
18328     * than a reference to a TreeNodeUI implementation, this that string value
18329     * is used as a property name in the uiProviders object. You can define the provider named
18330     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18331     */
18332     uiProviders : {},
18333
18334     /**
18335     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18336     * child nodes before loading.
18337     */
18338     clearOnLoad : true,
18339
18340     /**
18341     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18342     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18343     * Grid query { data : [ .....] }
18344     */
18345     
18346     root : false,
18347      /**
18348     * @cfg {String} queryParam (optional) 
18349     * Name of the query as it will be passed on the querystring (defaults to 'node')
18350     * eg. the request will be ?node=[id]
18351     */
18352     
18353     
18354     queryParam: false,
18355     
18356     /**
18357      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18358      * This is called automatically when a node is expanded, but may be used to reload
18359      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18360      * @param {Roo.tree.TreeNode} node
18361      * @param {Function} callback
18362      */
18363     load : function(node, callback){
18364         if(this.clearOnLoad){
18365             while(node.firstChild){
18366                 node.removeChild(node.firstChild);
18367             }
18368         }
18369         if(node.attributes.children){ // preloaded json children
18370             var cs = node.attributes.children;
18371             for(var i = 0, len = cs.length; i < len; i++){
18372                 node.appendChild(this.createNode(cs[i]));
18373             }
18374             if(typeof callback == "function"){
18375                 callback();
18376             }
18377         }else if(this.dataUrl){
18378             this.requestData(node, callback);
18379         }
18380     },
18381
18382     getParams: function(node){
18383         var buf = [], bp = this.baseParams;
18384         for(var key in bp){
18385             if(typeof bp[key] != "function"){
18386                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18387             }
18388         }
18389         var n = this.queryParam === false ? 'node' : this.queryParam;
18390         buf.push(n + "=", encodeURIComponent(node.id));
18391         return buf.join("");
18392     },
18393
18394     requestData : function(node, callback){
18395         if(this.fireEvent("beforeload", this, node, callback) !== false){
18396             this.transId = Roo.Ajax.request({
18397                 method:this.requestMethod,
18398                 url: this.dataUrl||this.url,
18399                 success: this.handleResponse,
18400                 failure: this.handleFailure,
18401                 scope: this,
18402                 argument: {callback: callback, node: node},
18403                 params: this.getParams(node)
18404             });
18405         }else{
18406             // if the load is cancelled, make sure we notify
18407             // the node that we are done
18408             if(typeof callback == "function"){
18409                 callback();
18410             }
18411         }
18412     },
18413
18414     isLoading : function(){
18415         return this.transId ? true : false;
18416     },
18417
18418     abort : function(){
18419         if(this.isLoading()){
18420             Roo.Ajax.abort(this.transId);
18421         }
18422     },
18423
18424     // private
18425     createNode : function(attr){
18426         // apply baseAttrs, nice idea Corey!
18427         if(this.baseAttrs){
18428             Roo.applyIf(attr, this.baseAttrs);
18429         }
18430         if(this.applyLoader !== false){
18431             attr.loader = this;
18432         }
18433         // uiProvider = depreciated..
18434         
18435         if(typeof(attr.uiProvider) == 'string'){
18436            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18437                 /**  eval:var:attr */ eval(attr.uiProvider);
18438         }
18439         if(typeof(this.uiProviders['default']) != 'undefined') {
18440             attr.uiProvider = this.uiProviders['default'];
18441         }
18442         
18443         this.fireEvent('create', this, attr);
18444         
18445         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18446         return(attr.leaf ?
18447                         new Roo.tree.TreeNode(attr) :
18448                         new Roo.tree.AsyncTreeNode(attr));
18449     },
18450
18451     processResponse : function(response, node, callback){
18452         var json = response.responseText;
18453         try {
18454             
18455             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18456             if (this.root !== false) {
18457                 o = o[this.root];
18458             }
18459             
18460             for(var i = 0, len = o.length; i < len; i++){
18461                 var n = this.createNode(o[i]);
18462                 if(n){
18463                     node.appendChild(n);
18464                 }
18465             }
18466             if(typeof callback == "function"){
18467                 callback(this, node);
18468             }
18469         }catch(e){
18470             this.handleFailure(response);
18471         }
18472     },
18473
18474     handleResponse : function(response){
18475         this.transId = false;
18476         var a = response.argument;
18477         this.processResponse(response, a.node, a.callback);
18478         this.fireEvent("load", this, a.node, response);
18479     },
18480
18481     handleFailure : function(response){
18482         this.transId = false;
18483         var a = response.argument;
18484         this.fireEvent("loadexception", this, a.node, response);
18485         if(typeof a.callback == "function"){
18486             a.callback(this, a.node);
18487         }
18488     }
18489 });/*
18490  * Based on:
18491  * Ext JS Library 1.1.1
18492  * Copyright(c) 2006-2007, Ext JS, LLC.
18493  *
18494  * Originally Released Under LGPL - original licence link has changed is not relivant.
18495  *
18496  * Fork - LGPL
18497  * <script type="text/javascript">
18498  */
18499
18500 /**
18501 * @class Roo.tree.TreeFilter
18502 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18503 * @param {TreePanel} tree
18504 * @param {Object} config (optional)
18505  */
18506 Roo.tree.TreeFilter = function(tree, config){
18507     this.tree = tree;
18508     this.filtered = {};
18509     Roo.apply(this, config);
18510 };
18511
18512 Roo.tree.TreeFilter.prototype = {
18513     clearBlank:false,
18514     reverse:false,
18515     autoClear:false,
18516     remove:false,
18517
18518      /**
18519      * Filter the data by a specific attribute.
18520      * @param {String/RegExp} value Either string that the attribute value
18521      * should start with or a RegExp to test against the attribute
18522      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18523      * @param {TreeNode} startNode (optional) The node to start the filter at.
18524      */
18525     filter : function(value, attr, startNode){
18526         attr = attr || "text";
18527         var f;
18528         if(typeof value == "string"){
18529             var vlen = value.length;
18530             // auto clear empty filter
18531             if(vlen == 0 && this.clearBlank){
18532                 this.clear();
18533                 return;
18534             }
18535             value = value.toLowerCase();
18536             f = function(n){
18537                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18538             };
18539         }else if(value.exec){ // regex?
18540             f = function(n){
18541                 return value.test(n.attributes[attr]);
18542             };
18543         }else{
18544             throw 'Illegal filter type, must be string or regex';
18545         }
18546         this.filterBy(f, null, startNode);
18547         },
18548
18549     /**
18550      * Filter by a function. The passed function will be called with each
18551      * node in the tree (or from the startNode). If the function returns true, the node is kept
18552      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18553      * @param {Function} fn The filter function
18554      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18555      */
18556     filterBy : function(fn, scope, startNode){
18557         startNode = startNode || this.tree.root;
18558         if(this.autoClear){
18559             this.clear();
18560         }
18561         var af = this.filtered, rv = this.reverse;
18562         var f = function(n){
18563             if(n == startNode){
18564                 return true;
18565             }
18566             if(af[n.id]){
18567                 return false;
18568             }
18569             var m = fn.call(scope || n, n);
18570             if(!m || rv){
18571                 af[n.id] = n;
18572                 n.ui.hide();
18573                 return false;
18574             }
18575             return true;
18576         };
18577         startNode.cascade(f);
18578         if(this.remove){
18579            for(var id in af){
18580                if(typeof id != "function"){
18581                    var n = af[id];
18582                    if(n && n.parentNode){
18583                        n.parentNode.removeChild(n);
18584                    }
18585                }
18586            }
18587         }
18588     },
18589
18590     /**
18591      * Clears the current filter. Note: with the "remove" option
18592      * set a filter cannot be cleared.
18593      */
18594     clear : function(){
18595         var t = this.tree;
18596         var af = this.filtered;
18597         for(var id in af){
18598             if(typeof id != "function"){
18599                 var n = af[id];
18600                 if(n){
18601                     n.ui.show();
18602                 }
18603             }
18604         }
18605         this.filtered = {};
18606     }
18607 };
18608 /*
18609  * Based on:
18610  * Ext JS Library 1.1.1
18611  * Copyright(c) 2006-2007, Ext JS, LLC.
18612  *
18613  * Originally Released Under LGPL - original licence link has changed is not relivant.
18614  *
18615  * Fork - LGPL
18616  * <script type="text/javascript">
18617  */
18618  
18619
18620 /**
18621  * @class Roo.tree.TreeSorter
18622  * Provides sorting of nodes in a TreePanel
18623  * 
18624  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18625  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18626  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18627  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18628  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18629  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18630  * @constructor
18631  * @param {TreePanel} tree
18632  * @param {Object} config
18633  */
18634 Roo.tree.TreeSorter = function(tree, config){
18635     Roo.apply(this, config);
18636     tree.on("beforechildrenrendered", this.doSort, this);
18637     tree.on("append", this.updateSort, this);
18638     tree.on("insert", this.updateSort, this);
18639     
18640     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18641     var p = this.property || "text";
18642     var sortType = this.sortType;
18643     var fs = this.folderSort;
18644     var cs = this.caseSensitive === true;
18645     var leafAttr = this.leafAttr || 'leaf';
18646
18647     this.sortFn = function(n1, n2){
18648         if(fs){
18649             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18650                 return 1;
18651             }
18652             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18653                 return -1;
18654             }
18655         }
18656         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18657         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18658         if(v1 < v2){
18659                         return dsc ? +1 : -1;
18660                 }else if(v1 > v2){
18661                         return dsc ? -1 : +1;
18662         }else{
18663                 return 0;
18664         }
18665     };
18666 };
18667
18668 Roo.tree.TreeSorter.prototype = {
18669     doSort : function(node){
18670         node.sort(this.sortFn);
18671     },
18672     
18673     compareNodes : function(n1, n2){
18674         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18675     },
18676     
18677     updateSort : function(tree, node){
18678         if(node.childrenRendered){
18679             this.doSort.defer(1, this, [node]);
18680         }
18681     }
18682 };/*
18683  * Based on:
18684  * Ext JS Library 1.1.1
18685  * Copyright(c) 2006-2007, Ext JS, LLC.
18686  *
18687  * Originally Released Under LGPL - original licence link has changed is not relivant.
18688  *
18689  * Fork - LGPL
18690  * <script type="text/javascript">
18691  */
18692
18693 if(Roo.dd.DropZone){
18694     
18695 Roo.tree.TreeDropZone = function(tree, config){
18696     this.allowParentInsert = false;
18697     this.allowContainerDrop = false;
18698     this.appendOnly = false;
18699     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18700     this.tree = tree;
18701     this.lastInsertClass = "x-tree-no-status";
18702     this.dragOverData = {};
18703 };
18704
18705 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18706     ddGroup : "TreeDD",
18707     
18708     expandDelay : 1000,
18709     
18710     expandNode : function(node){
18711         if(node.hasChildNodes() && !node.isExpanded()){
18712             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18713         }
18714     },
18715     
18716     queueExpand : function(node){
18717         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18718     },
18719     
18720     cancelExpand : function(){
18721         if(this.expandProcId){
18722             clearTimeout(this.expandProcId);
18723             this.expandProcId = false;
18724         }
18725     },
18726     
18727     isValidDropPoint : function(n, pt, dd, e, data){
18728         if(!n || !data){ return false; }
18729         var targetNode = n.node;
18730         var dropNode = data.node;
18731         // default drop rules
18732         if(!(targetNode && targetNode.isTarget && pt)){
18733             return false;
18734         }
18735         if(pt == "append" && targetNode.allowChildren === false){
18736             return false;
18737         }
18738         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18739             return false;
18740         }
18741         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18742             return false;
18743         }
18744         // reuse the object
18745         var overEvent = this.dragOverData;
18746         overEvent.tree = this.tree;
18747         overEvent.target = targetNode;
18748         overEvent.data = data;
18749         overEvent.point = pt;
18750         overEvent.source = dd;
18751         overEvent.rawEvent = e;
18752         overEvent.dropNode = dropNode;
18753         overEvent.cancel = false;  
18754         var result = this.tree.fireEvent("nodedragover", overEvent);
18755         return overEvent.cancel === false && result !== false;
18756     },
18757     
18758     getDropPoint : function(e, n, dd){
18759         var tn = n.node;
18760         if(tn.isRoot){
18761             return tn.allowChildren !== false ? "append" : false; // always append for root
18762         }
18763         var dragEl = n.ddel;
18764         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18765         var y = Roo.lib.Event.getPageY(e);
18766         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18767         
18768         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18769         var noAppend = tn.allowChildren === false;
18770         if(this.appendOnly || tn.parentNode.allowChildren === false){
18771             return noAppend ? false : "append";
18772         }
18773         var noBelow = false;
18774         if(!this.allowParentInsert){
18775             noBelow = tn.hasChildNodes() && tn.isExpanded();
18776         }
18777         var q = (b - t) / (noAppend ? 2 : 3);
18778         if(y >= t && y < (t + q)){
18779             return "above";
18780         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18781             return "below";
18782         }else{
18783             return "append";
18784         }
18785     },
18786     
18787     onNodeEnter : function(n, dd, e, data){
18788         this.cancelExpand();
18789     },
18790     
18791     onNodeOver : function(n, dd, e, data){
18792         var pt = this.getDropPoint(e, n, dd);
18793         var node = n.node;
18794         
18795         // auto node expand check
18796         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18797             this.queueExpand(node);
18798         }else if(pt != "append"){
18799             this.cancelExpand();
18800         }
18801         
18802         // set the insert point style on the target node
18803         var returnCls = this.dropNotAllowed;
18804         if(this.isValidDropPoint(n, pt, dd, e, data)){
18805            if(pt){
18806                var el = n.ddel;
18807                var cls;
18808                if(pt == "above"){
18809                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18810                    cls = "x-tree-drag-insert-above";
18811                }else if(pt == "below"){
18812                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18813                    cls = "x-tree-drag-insert-below";
18814                }else{
18815                    returnCls = "x-tree-drop-ok-append";
18816                    cls = "x-tree-drag-append";
18817                }
18818                if(this.lastInsertClass != cls){
18819                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18820                    this.lastInsertClass = cls;
18821                }
18822            }
18823        }
18824        return returnCls;
18825     },
18826     
18827     onNodeOut : function(n, dd, e, data){
18828         this.cancelExpand();
18829         this.removeDropIndicators(n);
18830     },
18831     
18832     onNodeDrop : function(n, dd, e, data){
18833         var point = this.getDropPoint(e, n, dd);
18834         var targetNode = n.node;
18835         targetNode.ui.startDrop();
18836         if(!this.isValidDropPoint(n, point, dd, e, data)){
18837             targetNode.ui.endDrop();
18838             return false;
18839         }
18840         // first try to find the drop node
18841         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18842         var dropEvent = {
18843             tree : this.tree,
18844             target: targetNode,
18845             data: data,
18846             point: point,
18847             source: dd,
18848             rawEvent: e,
18849             dropNode: dropNode,
18850             cancel: !dropNode   
18851         };
18852         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18853         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18854             targetNode.ui.endDrop();
18855             return false;
18856         }
18857         // allow target changing
18858         targetNode = dropEvent.target;
18859         if(point == "append" && !targetNode.isExpanded()){
18860             targetNode.expand(false, null, function(){
18861                 this.completeDrop(dropEvent);
18862             }.createDelegate(this));
18863         }else{
18864             this.completeDrop(dropEvent);
18865         }
18866         return true;
18867     },
18868     
18869     completeDrop : function(de){
18870         var ns = de.dropNode, p = de.point, t = de.target;
18871         if(!(ns instanceof Array)){
18872             ns = [ns];
18873         }
18874         var n;
18875         for(var i = 0, len = ns.length; i < len; i++){
18876             n = ns[i];
18877             if(p == "above"){
18878                 t.parentNode.insertBefore(n, t);
18879             }else if(p == "below"){
18880                 t.parentNode.insertBefore(n, t.nextSibling);
18881             }else{
18882                 t.appendChild(n);
18883             }
18884         }
18885         n.ui.focus();
18886         if(this.tree.hlDrop){
18887             n.ui.highlight();
18888         }
18889         t.ui.endDrop();
18890         this.tree.fireEvent("nodedrop", de);
18891     },
18892     
18893     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18894         if(this.tree.hlDrop){
18895             dropNode.ui.focus();
18896             dropNode.ui.highlight();
18897         }
18898         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18899     },
18900     
18901     getTree : function(){
18902         return this.tree;
18903     },
18904     
18905     removeDropIndicators : function(n){
18906         if(n && n.ddel){
18907             var el = n.ddel;
18908             Roo.fly(el).removeClass([
18909                     "x-tree-drag-insert-above",
18910                     "x-tree-drag-insert-below",
18911                     "x-tree-drag-append"]);
18912             this.lastInsertClass = "_noclass";
18913         }
18914     },
18915     
18916     beforeDragDrop : function(target, e, id){
18917         this.cancelExpand();
18918         return true;
18919     },
18920     
18921     afterRepair : function(data){
18922         if(data && Roo.enableFx){
18923             data.node.ui.highlight();
18924         }
18925         this.hideProxy();
18926     }    
18927 });
18928
18929 }
18930 /*
18931  * Based on:
18932  * Ext JS Library 1.1.1
18933  * Copyright(c) 2006-2007, Ext JS, LLC.
18934  *
18935  * Originally Released Under LGPL - original licence link has changed is not relivant.
18936  *
18937  * Fork - LGPL
18938  * <script type="text/javascript">
18939  */
18940  
18941
18942 if(Roo.dd.DragZone){
18943 Roo.tree.TreeDragZone = function(tree, config){
18944     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18945     this.tree = tree;
18946 };
18947
18948 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18949     ddGroup : "TreeDD",
18950     
18951     onBeforeDrag : function(data, e){
18952         var n = data.node;
18953         return n && n.draggable && !n.disabled;
18954     },
18955     
18956     onInitDrag : function(e){
18957         var data = this.dragData;
18958         this.tree.getSelectionModel().select(data.node);
18959         this.proxy.update("");
18960         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18961         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18962     },
18963     
18964     getRepairXY : function(e, data){
18965         return data.node.ui.getDDRepairXY();
18966     },
18967     
18968     onEndDrag : function(data, e){
18969         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18970     },
18971     
18972     onValidDrop : function(dd, e, id){
18973         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18974         this.hideProxy();
18975     },
18976     
18977     beforeInvalidDrop : function(e, id){
18978         // this scrolls the original position back into view
18979         var sm = this.tree.getSelectionModel();
18980         sm.clearSelections();
18981         sm.select(this.dragData.node);
18982     }
18983 });
18984 }/*
18985  * Based on:
18986  * Ext JS Library 1.1.1
18987  * Copyright(c) 2006-2007, Ext JS, LLC.
18988  *
18989  * Originally Released Under LGPL - original licence link has changed is not relivant.
18990  *
18991  * Fork - LGPL
18992  * <script type="text/javascript">
18993  */
18994 /**
18995  * @class Roo.tree.TreeEditor
18996  * @extends Roo.Editor
18997  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18998  * as the editor field.
18999  * @constructor
19000  * @param {TreePanel} tree
19001  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19002  */
19003 Roo.tree.TreeEditor = function(tree, config){
19004     config = config || {};
19005     var field = config.events ? config : new Roo.form.TextField(config);
19006     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
19007
19008     this.tree = tree;
19009
19010     tree.on('beforeclick', this.beforeNodeClick, this);
19011     tree.getTreeEl().on('mousedown', this.hide, this);
19012     this.on('complete', this.updateNode, this);
19013     this.on('beforestartedit', this.fitToTree, this);
19014     this.on('startedit', this.bindScroll, this, {delay:10});
19015     this.on('specialkey', this.onSpecialKey, this);
19016 };
19017
19018 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19019     /**
19020      * @cfg {String} alignment
19021      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19022      */
19023     alignment: "l-l",
19024     // inherit
19025     autoSize: false,
19026     /**
19027      * @cfg {Boolean} hideEl
19028      * True to hide the bound element while the editor is displayed (defaults to false)
19029      */
19030     hideEl : false,
19031     /**
19032      * @cfg {String} cls
19033      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19034      */
19035     cls: "x-small-editor x-tree-editor",
19036     /**
19037      * @cfg {Boolean} shim
19038      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19039      */
19040     shim:false,
19041     // inherit
19042     shadow:"frame",
19043     /**
19044      * @cfg {Number} maxWidth
19045      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19046      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19047      * scroll and client offsets into account prior to each edit.
19048      */
19049     maxWidth: 250,
19050
19051     editDelay : 350,
19052
19053     // private
19054     fitToTree : function(ed, el){
19055         var td = this.tree.getTreeEl().dom, nd = el.dom;
19056         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19057             td.scrollLeft = nd.offsetLeft;
19058         }
19059         var w = Math.min(
19060                 this.maxWidth,
19061                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19062         this.setSize(w, '');
19063     },
19064
19065     // private
19066     triggerEdit : function(node){
19067         this.completeEdit();
19068         this.editNode = node;
19069         this.startEdit(node.ui.textNode, node.text);
19070     },
19071
19072     // private
19073     bindScroll : function(){
19074         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19075     },
19076
19077     // private
19078     beforeNodeClick : function(node, e){
19079         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19080         this.lastClick = new Date();
19081         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19082             e.stopEvent();
19083             this.triggerEdit(node);
19084             return false;
19085         }
19086     },
19087
19088     // private
19089     updateNode : function(ed, value){
19090         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19091         this.editNode.setText(value);
19092     },
19093
19094     // private
19095     onHide : function(){
19096         Roo.tree.TreeEditor.superclass.onHide.call(this);
19097         if(this.editNode){
19098             this.editNode.ui.focus();
19099         }
19100     },
19101
19102     // private
19103     onSpecialKey : function(field, e){
19104         var k = e.getKey();
19105         if(k == e.ESC){
19106             e.stopEvent();
19107             this.cancelEdit();
19108         }else if(k == e.ENTER && !e.hasModifier()){
19109             e.stopEvent();
19110             this.completeEdit();
19111         }
19112     }
19113 });//<Script type="text/javascript">
19114 /*
19115  * Based on:
19116  * Ext JS Library 1.1.1
19117  * Copyright(c) 2006-2007, Ext JS, LLC.
19118  *
19119  * Originally Released Under LGPL - original licence link has changed is not relivant.
19120  *
19121  * Fork - LGPL
19122  * <script type="text/javascript">
19123  */
19124  
19125 /**
19126  * Not documented??? - probably should be...
19127  */
19128
19129 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19130     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19131     
19132     renderElements : function(n, a, targetNode, bulkRender){
19133         //consel.log("renderElements?");
19134         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19135
19136         var t = n.getOwnerTree();
19137         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19138         
19139         var cols = t.columns;
19140         var bw = t.borderWidth;
19141         var c = cols[0];
19142         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19143          var cb = typeof a.checked == "boolean";
19144         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19145         var colcls = 'x-t-' + tid + '-c0';
19146         var buf = [
19147             '<li class="x-tree-node">',
19148             
19149                 
19150                 '<div class="x-tree-node-el ', a.cls,'">',
19151                     // extran...
19152                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19153                 
19154                 
19155                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19156                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19157                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19158                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19159                            (a.iconCls ? ' '+a.iconCls : ''),
19160                            '" unselectable="on" />',
19161                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19162                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19163                              
19164                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19165                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19166                             '<span unselectable="on" qtip="' + tx + '">',
19167                              tx,
19168                              '</span></a>' ,
19169                     '</div>',
19170                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19171                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19172                  ];
19173         for(var i = 1, len = cols.length; i < len; i++){
19174             c = cols[i];
19175             colcls = 'x-t-' + tid + '-c' +i;
19176             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19177             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19178                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19179                       "</div>");
19180          }
19181          
19182          buf.push(
19183             '</a>',
19184             '<div class="x-clear"></div></div>',
19185             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19186             "</li>");
19187         
19188         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19189             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19190                                 n.nextSibling.ui.getEl(), buf.join(""));
19191         }else{
19192             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19193         }
19194         var el = this.wrap.firstChild;
19195         this.elRow = el;
19196         this.elNode = el.firstChild;
19197         this.ranchor = el.childNodes[1];
19198         this.ctNode = this.wrap.childNodes[1];
19199         var cs = el.firstChild.childNodes;
19200         this.indentNode = cs[0];
19201         this.ecNode = cs[1];
19202         this.iconNode = cs[2];
19203         var index = 3;
19204         if(cb){
19205             this.checkbox = cs[3];
19206             index++;
19207         }
19208         this.anchor = cs[index];
19209         
19210         this.textNode = cs[index].firstChild;
19211         
19212         //el.on("click", this.onClick, this);
19213         //el.on("dblclick", this.onDblClick, this);
19214         
19215         
19216        // console.log(this);
19217     },
19218     initEvents : function(){
19219         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19220         
19221             
19222         var a = this.ranchor;
19223
19224         var el = Roo.get(a);
19225
19226         if(Roo.isOpera){ // opera render bug ignores the CSS
19227             el.setStyle("text-decoration", "none");
19228         }
19229
19230         el.on("click", this.onClick, this);
19231         el.on("dblclick", this.onDblClick, this);
19232         el.on("contextmenu", this.onContextMenu, this);
19233         
19234     },
19235     
19236     /*onSelectedChange : function(state){
19237         if(state){
19238             this.focus();
19239             this.addClass("x-tree-selected");
19240         }else{
19241             //this.blur();
19242             this.removeClass("x-tree-selected");
19243         }
19244     },*/
19245     addClass : function(cls){
19246         if(this.elRow){
19247             Roo.fly(this.elRow).addClass(cls);
19248         }
19249         
19250     },
19251     
19252     
19253     removeClass : function(cls){
19254         if(this.elRow){
19255             Roo.fly(this.elRow).removeClass(cls);
19256         }
19257     }
19258
19259     
19260     
19261 });//<Script type="text/javascript">
19262
19263 /*
19264  * Based on:
19265  * Ext JS Library 1.1.1
19266  * Copyright(c) 2006-2007, Ext JS, LLC.
19267  *
19268  * Originally Released Under LGPL - original licence link has changed is not relivant.
19269  *
19270  * Fork - LGPL
19271  * <script type="text/javascript">
19272  */
19273  
19274
19275 /**
19276  * @class Roo.tree.ColumnTree
19277  * @extends Roo.data.TreePanel
19278  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19279  * @cfg {int} borderWidth  compined right/left border allowance
19280  * @constructor
19281  * @param {String/HTMLElement/Element} el The container element
19282  * @param {Object} config
19283  */
19284 Roo.tree.ColumnTree =  function(el, config)
19285 {
19286    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19287    this.addEvents({
19288         /**
19289         * @event resize
19290         * Fire this event on a container when it resizes
19291         * @param {int} w Width
19292         * @param {int} h Height
19293         */
19294        "resize" : true
19295     });
19296     this.on('resize', this.onResize, this);
19297 };
19298
19299 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19300     //lines:false,
19301     
19302     
19303     borderWidth: Roo.isBorderBox ? 0 : 2, 
19304     headEls : false,
19305     
19306     render : function(){
19307         // add the header.....
19308        
19309         Roo.tree.ColumnTree.superclass.render.apply(this);
19310         
19311         this.el.addClass('x-column-tree');
19312         
19313         this.headers = this.el.createChild(
19314             {cls:'x-tree-headers'},this.innerCt.dom);
19315    
19316         var cols = this.columns, c;
19317         var totalWidth = 0;
19318         this.headEls = [];
19319         var  len = cols.length;
19320         for(var i = 0; i < len; i++){
19321              c = cols[i];
19322              totalWidth += c.width;
19323             this.headEls.push(this.headers.createChild({
19324                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19325                  cn: {
19326                      cls:'x-tree-hd-text',
19327                      html: c.header
19328                  },
19329                  style:'width:'+(c.width-this.borderWidth)+'px;'
19330              }));
19331         }
19332         this.headers.createChild({cls:'x-clear'});
19333         // prevent floats from wrapping when clipped
19334         this.headers.setWidth(totalWidth);
19335         //this.innerCt.setWidth(totalWidth);
19336         this.innerCt.setStyle({ overflow: 'auto' });
19337         this.onResize(this.width, this.height);
19338              
19339         
19340     },
19341     onResize : function(w,h)
19342     {
19343         this.height = h;
19344         this.width = w;
19345         // resize cols..
19346         this.innerCt.setWidth(this.width);
19347         this.innerCt.setHeight(this.height-20);
19348         
19349         // headers...
19350         var cols = this.columns, c;
19351         var totalWidth = 0;
19352         var expEl = false;
19353         var len = cols.length;
19354         for(var i = 0; i < len; i++){
19355             c = cols[i];
19356             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19357                 // it's the expander..
19358                 expEl  = this.headEls[i];
19359                 continue;
19360             }
19361             totalWidth += c.width;
19362             
19363         }
19364         if (expEl) {
19365             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19366         }
19367         this.headers.setWidth(w-20);
19368
19369         
19370         
19371         
19372     }
19373 });
19374 /*
19375  * Based on:
19376  * Ext JS Library 1.1.1
19377  * Copyright(c) 2006-2007, Ext JS, LLC.
19378  *
19379  * Originally Released Under LGPL - original licence link has changed is not relivant.
19380  *
19381  * Fork - LGPL
19382  * <script type="text/javascript">
19383  */
19384  
19385 /**
19386  * @class Roo.menu.Menu
19387  * @extends Roo.util.Observable
19388  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19389  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19390  * @constructor
19391  * Creates a new Menu
19392  * @param {Object} config Configuration options
19393  */
19394 Roo.menu.Menu = function(config){
19395     Roo.apply(this, config);
19396     this.id = this.id || Roo.id();
19397     this.addEvents({
19398         /**
19399          * @event beforeshow
19400          * Fires before this menu is displayed
19401          * @param {Roo.menu.Menu} this
19402          */
19403         beforeshow : true,
19404         /**
19405          * @event beforehide
19406          * Fires before this menu is hidden
19407          * @param {Roo.menu.Menu} this
19408          */
19409         beforehide : true,
19410         /**
19411          * @event show
19412          * Fires after this menu is displayed
19413          * @param {Roo.menu.Menu} this
19414          */
19415         show : true,
19416         /**
19417          * @event hide
19418          * Fires after this menu is hidden
19419          * @param {Roo.menu.Menu} this
19420          */
19421         hide : true,
19422         /**
19423          * @event click
19424          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19425          * @param {Roo.menu.Menu} this
19426          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19427          * @param {Roo.EventObject} e
19428          */
19429         click : true,
19430         /**
19431          * @event mouseover
19432          * Fires when the mouse is hovering over this menu
19433          * @param {Roo.menu.Menu} this
19434          * @param {Roo.EventObject} e
19435          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19436          */
19437         mouseover : true,
19438         /**
19439          * @event mouseout
19440          * Fires when the mouse exits this menu
19441          * @param {Roo.menu.Menu} this
19442          * @param {Roo.EventObject} e
19443          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19444          */
19445         mouseout : true,
19446         /**
19447          * @event itemclick
19448          * Fires when a menu item contained in this menu is clicked
19449          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19450          * @param {Roo.EventObject} e
19451          */
19452         itemclick: true
19453     });
19454     if (this.registerMenu) {
19455         Roo.menu.MenuMgr.register(this);
19456     }
19457     
19458     var mis = this.items;
19459     this.items = new Roo.util.MixedCollection();
19460     if(mis){
19461         this.add.apply(this, mis);
19462     }
19463 };
19464
19465 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19466     /**
19467      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19468      */
19469     minWidth : 120,
19470     /**
19471      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19472      * for bottom-right shadow (defaults to "sides")
19473      */
19474     shadow : "sides",
19475     /**
19476      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19477      * this menu (defaults to "tl-tr?")
19478      */
19479     subMenuAlign : "tl-tr?",
19480     /**
19481      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19482      * relative to its element of origin (defaults to "tl-bl?")
19483      */
19484     defaultAlign : "tl-bl?",
19485     /**
19486      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19487      */
19488     allowOtherMenus : false,
19489     /**
19490      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19491      */
19492     registerMenu : true,
19493
19494     hidden:true,
19495
19496     // private
19497     render : function(){
19498         if(this.el){
19499             return;
19500         }
19501         var el = this.el = new Roo.Layer({
19502             cls: "x-menu",
19503             shadow:this.shadow,
19504             constrain: false,
19505             parentEl: this.parentEl || document.body,
19506             zindex:15000
19507         });
19508
19509         this.keyNav = new Roo.menu.MenuNav(this);
19510
19511         if(this.plain){
19512             el.addClass("x-menu-plain");
19513         }
19514         if(this.cls){
19515             el.addClass(this.cls);
19516         }
19517         // generic focus element
19518         this.focusEl = el.createChild({
19519             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19520         });
19521         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19522         ul.on("click", this.onClick, this);
19523         ul.on("mouseover", this.onMouseOver, this);
19524         ul.on("mouseout", this.onMouseOut, this);
19525         this.items.each(function(item){
19526             var li = document.createElement("li");
19527             li.className = "x-menu-list-item";
19528             ul.dom.appendChild(li);
19529             item.render(li, this);
19530         }, this);
19531         this.ul = ul;
19532         this.autoWidth();
19533     },
19534
19535     // private
19536     autoWidth : function(){
19537         var el = this.el, ul = this.ul;
19538         if(!el){
19539             return;
19540         }
19541         var w = this.width;
19542         if(w){
19543             el.setWidth(w);
19544         }else if(Roo.isIE){
19545             el.setWidth(this.minWidth);
19546             var t = el.dom.offsetWidth; // force recalc
19547             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19548         }
19549     },
19550
19551     // private
19552     delayAutoWidth : function(){
19553         if(this.rendered){
19554             if(!this.awTask){
19555                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19556             }
19557             this.awTask.delay(20);
19558         }
19559     },
19560
19561     // private
19562     findTargetItem : function(e){
19563         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19564         if(t && t.menuItemId){
19565             return this.items.get(t.menuItemId);
19566         }
19567     },
19568
19569     // private
19570     onClick : function(e){
19571         var t;
19572         if(t = this.findTargetItem(e)){
19573             t.onClick(e);
19574             this.fireEvent("click", this, t, e);
19575         }
19576     },
19577
19578     // private
19579     setActiveItem : function(item, autoExpand){
19580         if(item != this.activeItem){
19581             if(this.activeItem){
19582                 this.activeItem.deactivate();
19583             }
19584             this.activeItem = item;
19585             item.activate(autoExpand);
19586         }else if(autoExpand){
19587             item.expandMenu();
19588         }
19589     },
19590
19591     // private
19592     tryActivate : function(start, step){
19593         var items = this.items;
19594         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19595             var item = items.get(i);
19596             if(!item.disabled && item.canActivate){
19597                 this.setActiveItem(item, false);
19598                 return item;
19599             }
19600         }
19601         return false;
19602     },
19603
19604     // private
19605     onMouseOver : function(e){
19606         var t;
19607         if(t = this.findTargetItem(e)){
19608             if(t.canActivate && !t.disabled){
19609                 this.setActiveItem(t, true);
19610             }
19611         }
19612         this.fireEvent("mouseover", this, e, t);
19613     },
19614
19615     // private
19616     onMouseOut : function(e){
19617         var t;
19618         if(t = this.findTargetItem(e)){
19619             if(t == this.activeItem && t.shouldDeactivate(e)){
19620                 this.activeItem.deactivate();
19621                 delete this.activeItem;
19622             }
19623         }
19624         this.fireEvent("mouseout", this, e, t);
19625     },
19626
19627     /**
19628      * Read-only.  Returns true if the menu is currently displayed, else false.
19629      * @type Boolean
19630      */
19631     isVisible : function(){
19632         return this.el && !this.hidden;
19633     },
19634
19635     /**
19636      * Displays this menu relative to another element
19637      * @param {String/HTMLElement/Roo.Element} element The element to align to
19638      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19639      * the element (defaults to this.defaultAlign)
19640      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19641      */
19642     show : function(el, pos, parentMenu){
19643         this.parentMenu = parentMenu;
19644         if(!this.el){
19645             this.render();
19646         }
19647         this.fireEvent("beforeshow", this);
19648         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19649     },
19650
19651     /**
19652      * Displays this menu at a specific xy position
19653      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19654      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19655      */
19656     showAt : function(xy, parentMenu, /* private: */_e){
19657         this.parentMenu = parentMenu;
19658         if(!this.el){
19659             this.render();
19660         }
19661         if(_e !== false){
19662             this.fireEvent("beforeshow", this);
19663             xy = this.el.adjustForConstraints(xy);
19664         }
19665         this.el.setXY(xy);
19666         this.el.show();
19667         this.hidden = false;
19668         this.focus();
19669         this.fireEvent("show", this);
19670     },
19671
19672     focus : function(){
19673         if(!this.hidden){
19674             this.doFocus.defer(50, this);
19675         }
19676     },
19677
19678     doFocus : function(){
19679         if(!this.hidden){
19680             this.focusEl.focus();
19681         }
19682     },
19683
19684     /**
19685      * Hides this menu and optionally all parent menus
19686      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19687      */
19688     hide : function(deep){
19689         if(this.el && this.isVisible()){
19690             this.fireEvent("beforehide", this);
19691             if(this.activeItem){
19692                 this.activeItem.deactivate();
19693                 this.activeItem = null;
19694             }
19695             this.el.hide();
19696             this.hidden = true;
19697             this.fireEvent("hide", this);
19698         }
19699         if(deep === true && this.parentMenu){
19700             this.parentMenu.hide(true);
19701         }
19702     },
19703
19704     /**
19705      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19706      * Any of the following are valid:
19707      * <ul>
19708      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19709      * <li>An HTMLElement object which will be converted to a menu item</li>
19710      * <li>A menu item config object that will be created as a new menu item</li>
19711      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19712      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19713      * </ul>
19714      * Usage:
19715      * <pre><code>
19716 // Create the menu
19717 var menu = new Roo.menu.Menu();
19718
19719 // Create a menu item to add by reference
19720 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19721
19722 // Add a bunch of items at once using different methods.
19723 // Only the last item added will be returned.
19724 var item = menu.add(
19725     menuItem,                // add existing item by ref
19726     'Dynamic Item',          // new TextItem
19727     '-',                     // new separator
19728     { text: 'Config Item' }  // new item by config
19729 );
19730 </code></pre>
19731      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19732      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19733      */
19734     add : function(){
19735         var a = arguments, l = a.length, item;
19736         for(var i = 0; i < l; i++){
19737             var el = a[i];
19738             if ((typeof(el) == "object") && el.xtype && el.xns) {
19739                 el = Roo.factory(el, Roo.menu);
19740             }
19741             
19742             if(el.render){ // some kind of Item
19743                 item = this.addItem(el);
19744             }else if(typeof el == "string"){ // string
19745                 if(el == "separator" || el == "-"){
19746                     item = this.addSeparator();
19747                 }else{
19748                     item = this.addText(el);
19749                 }
19750             }else if(el.tagName || el.el){ // element
19751                 item = this.addElement(el);
19752             }else if(typeof el == "object"){ // must be menu item config?
19753                 item = this.addMenuItem(el);
19754             }
19755         }
19756         return item;
19757     },
19758
19759     /**
19760      * Returns this menu's underlying {@link Roo.Element} object
19761      * @return {Roo.Element} The element
19762      */
19763     getEl : function(){
19764         if(!this.el){
19765             this.render();
19766         }
19767         return this.el;
19768     },
19769
19770     /**
19771      * Adds a separator bar to the menu
19772      * @return {Roo.menu.Item} The menu item that was added
19773      */
19774     addSeparator : function(){
19775         return this.addItem(new Roo.menu.Separator());
19776     },
19777
19778     /**
19779      * Adds an {@link Roo.Element} object to the menu
19780      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19781      * @return {Roo.menu.Item} The menu item that was added
19782      */
19783     addElement : function(el){
19784         return this.addItem(new Roo.menu.BaseItem(el));
19785     },
19786
19787     /**
19788      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19789      * @param {Roo.menu.Item} item The menu item to add
19790      * @return {Roo.menu.Item} The menu item that was added
19791      */
19792     addItem : function(item){
19793         this.items.add(item);
19794         if(this.ul){
19795             var li = document.createElement("li");
19796             li.className = "x-menu-list-item";
19797             this.ul.dom.appendChild(li);
19798             item.render(li, this);
19799             this.delayAutoWidth();
19800         }
19801         return item;
19802     },
19803
19804     /**
19805      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19806      * @param {Object} config A MenuItem config object
19807      * @return {Roo.menu.Item} The menu item that was added
19808      */
19809     addMenuItem : function(config){
19810         if(!(config instanceof Roo.menu.Item)){
19811             if(typeof config.checked == "boolean"){ // must be check menu item config?
19812                 config = new Roo.menu.CheckItem(config);
19813             }else{
19814                 config = new Roo.menu.Item(config);
19815             }
19816         }
19817         return this.addItem(config);
19818     },
19819
19820     /**
19821      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19822      * @param {String} text The text to display in the menu item
19823      * @return {Roo.menu.Item} The menu item that was added
19824      */
19825     addText : function(text){
19826         return this.addItem(new Roo.menu.TextItem({ text : text }));
19827     },
19828
19829     /**
19830      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19831      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19832      * @param {Roo.menu.Item} item The menu item to add
19833      * @return {Roo.menu.Item} The menu item that was added
19834      */
19835     insert : function(index, item){
19836         this.items.insert(index, item);
19837         if(this.ul){
19838             var li = document.createElement("li");
19839             li.className = "x-menu-list-item";
19840             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19841             item.render(li, this);
19842             this.delayAutoWidth();
19843         }
19844         return item;
19845     },
19846
19847     /**
19848      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19849      * @param {Roo.menu.Item} item The menu item to remove
19850      */
19851     remove : function(item){
19852         this.items.removeKey(item.id);
19853         item.destroy();
19854     },
19855
19856     /**
19857      * Removes and destroys all items in the menu
19858      */
19859     removeAll : function(){
19860         var f;
19861         while(f = this.items.first()){
19862             this.remove(f);
19863         }
19864     }
19865 });
19866
19867 // MenuNav is a private utility class used internally by the Menu
19868 Roo.menu.MenuNav = function(menu){
19869     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19870     this.scope = this.menu = menu;
19871 };
19872
19873 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19874     doRelay : function(e, h){
19875         var k = e.getKey();
19876         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19877             this.menu.tryActivate(0, 1);
19878             return false;
19879         }
19880         return h.call(this.scope || this, e, this.menu);
19881     },
19882
19883     up : function(e, m){
19884         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19885             m.tryActivate(m.items.length-1, -1);
19886         }
19887     },
19888
19889     down : function(e, m){
19890         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19891             m.tryActivate(0, 1);
19892         }
19893     },
19894
19895     right : function(e, m){
19896         if(m.activeItem){
19897             m.activeItem.expandMenu(true);
19898         }
19899     },
19900
19901     left : function(e, m){
19902         m.hide();
19903         if(m.parentMenu && m.parentMenu.activeItem){
19904             m.parentMenu.activeItem.activate();
19905         }
19906     },
19907
19908     enter : function(e, m){
19909         if(m.activeItem){
19910             e.stopPropagation();
19911             m.activeItem.onClick(e);
19912             m.fireEvent("click", this, m.activeItem);
19913             return true;
19914         }
19915     }
19916 });/*
19917  * Based on:
19918  * Ext JS Library 1.1.1
19919  * Copyright(c) 2006-2007, Ext JS, LLC.
19920  *
19921  * Originally Released Under LGPL - original licence link has changed is not relivant.
19922  *
19923  * Fork - LGPL
19924  * <script type="text/javascript">
19925  */
19926  
19927 /**
19928  * @class Roo.menu.MenuMgr
19929  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19930  * @singleton
19931  */
19932 Roo.menu.MenuMgr = function(){
19933    var menus, active, groups = {}, attached = false, lastShow = new Date();
19934
19935    // private - called when first menu is created
19936    function init(){
19937        menus = {};
19938        active = new Roo.util.MixedCollection();
19939        Roo.get(document).addKeyListener(27, function(){
19940            if(active.length > 0){
19941                hideAll();
19942            }
19943        });
19944    }
19945
19946    // private
19947    function hideAll(){
19948        if(active && active.length > 0){
19949            var c = active.clone();
19950            c.each(function(m){
19951                m.hide();
19952            });
19953        }
19954    }
19955
19956    // private
19957    function onHide(m){
19958        active.remove(m);
19959        if(active.length < 1){
19960            Roo.get(document).un("mousedown", onMouseDown);
19961            attached = false;
19962        }
19963    }
19964
19965    // private
19966    function onShow(m){
19967        var last = active.last();
19968        lastShow = new Date();
19969        active.add(m);
19970        if(!attached){
19971            Roo.get(document).on("mousedown", onMouseDown);
19972            attached = true;
19973        }
19974        if(m.parentMenu){
19975           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19976           m.parentMenu.activeChild = m;
19977        }else if(last && last.isVisible()){
19978           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19979        }
19980    }
19981
19982    // private
19983    function onBeforeHide(m){
19984        if(m.activeChild){
19985            m.activeChild.hide();
19986        }
19987        if(m.autoHideTimer){
19988            clearTimeout(m.autoHideTimer);
19989            delete m.autoHideTimer;
19990        }
19991    }
19992
19993    // private
19994    function onBeforeShow(m){
19995        var pm = m.parentMenu;
19996        if(!pm && !m.allowOtherMenus){
19997            hideAll();
19998        }else if(pm && pm.activeChild && active != m){
19999            pm.activeChild.hide();
20000        }
20001    }
20002
20003    // private
20004    function onMouseDown(e){
20005        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20006            hideAll();
20007        }
20008    }
20009
20010    // private
20011    function onBeforeCheck(mi, state){
20012        if(state){
20013            var g = groups[mi.group];
20014            for(var i = 0, l = g.length; i < l; i++){
20015                if(g[i] != mi){
20016                    g[i].setChecked(false);
20017                }
20018            }
20019        }
20020    }
20021
20022    return {
20023
20024        /**
20025         * Hides all menus that are currently visible
20026         */
20027        hideAll : function(){
20028             hideAll();  
20029        },
20030
20031        // private
20032        register : function(menu){
20033            if(!menus){
20034                init();
20035            }
20036            menus[menu.id] = menu;
20037            menu.on("beforehide", onBeforeHide);
20038            menu.on("hide", onHide);
20039            menu.on("beforeshow", onBeforeShow);
20040            menu.on("show", onShow);
20041            var g = menu.group;
20042            if(g && menu.events["checkchange"]){
20043                if(!groups[g]){
20044                    groups[g] = [];
20045                }
20046                groups[g].push(menu);
20047                menu.on("checkchange", onCheck);
20048            }
20049        },
20050
20051         /**
20052          * Returns a {@link Roo.menu.Menu} object
20053          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20054          * be used to generate and return a new Menu instance.
20055          */
20056        get : function(menu){
20057            if(typeof menu == "string"){ // menu id
20058                return menus[menu];
20059            }else if(menu.events){  // menu instance
20060                return menu;
20061            }else if(typeof menu.length == 'number'){ // array of menu items?
20062                return new Roo.menu.Menu({items:menu});
20063            }else{ // otherwise, must be a config
20064                return new Roo.menu.Menu(menu);
20065            }
20066        },
20067
20068        // private
20069        unregister : function(menu){
20070            delete menus[menu.id];
20071            menu.un("beforehide", onBeforeHide);
20072            menu.un("hide", onHide);
20073            menu.un("beforeshow", onBeforeShow);
20074            menu.un("show", onShow);
20075            var g = menu.group;
20076            if(g && menu.events["checkchange"]){
20077                groups[g].remove(menu);
20078                menu.un("checkchange", onCheck);
20079            }
20080        },
20081
20082        // private
20083        registerCheckable : function(menuItem){
20084            var g = menuItem.group;
20085            if(g){
20086                if(!groups[g]){
20087                    groups[g] = [];
20088                }
20089                groups[g].push(menuItem);
20090                menuItem.on("beforecheckchange", onBeforeCheck);
20091            }
20092        },
20093
20094        // private
20095        unregisterCheckable : function(menuItem){
20096            var g = menuItem.group;
20097            if(g){
20098                groups[g].remove(menuItem);
20099                menuItem.un("beforecheckchange", onBeforeCheck);
20100            }
20101        }
20102    };
20103 }();/*
20104  * Based on:
20105  * Ext JS Library 1.1.1
20106  * Copyright(c) 2006-2007, Ext JS, LLC.
20107  *
20108  * Originally Released Under LGPL - original licence link has changed is not relivant.
20109  *
20110  * Fork - LGPL
20111  * <script type="text/javascript">
20112  */
20113  
20114
20115 /**
20116  * @class Roo.menu.BaseItem
20117  * @extends Roo.Component
20118  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20119  * management and base configuration options shared by all menu components.
20120  * @constructor
20121  * Creates a new BaseItem
20122  * @param {Object} config Configuration options
20123  */
20124 Roo.menu.BaseItem = function(config){
20125     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20126
20127     this.addEvents({
20128         /**
20129          * @event click
20130          * Fires when this item is clicked
20131          * @param {Roo.menu.BaseItem} this
20132          * @param {Roo.EventObject} e
20133          */
20134         click: true,
20135         /**
20136          * @event activate
20137          * Fires when this item is activated
20138          * @param {Roo.menu.BaseItem} this
20139          */
20140         activate : true,
20141         /**
20142          * @event deactivate
20143          * Fires when this item is deactivated
20144          * @param {Roo.menu.BaseItem} this
20145          */
20146         deactivate : true
20147     });
20148
20149     if(this.handler){
20150         this.on("click", this.handler, this.scope, true);
20151     }
20152 };
20153
20154 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20155     /**
20156      * @cfg {Function} handler
20157      * A function that will handle the click event of this menu item (defaults to undefined)
20158      */
20159     /**
20160      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20161      */
20162     canActivate : false,
20163     /**
20164      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20165      */
20166     activeClass : "x-menu-item-active",
20167     /**
20168      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20169      */
20170     hideOnClick : true,
20171     /**
20172      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20173      */
20174     hideDelay : 100,
20175
20176     // private
20177     ctype: "Roo.menu.BaseItem",
20178
20179     // private
20180     actionMode : "container",
20181
20182     // private
20183     render : function(container, parentMenu){
20184         this.parentMenu = parentMenu;
20185         Roo.menu.BaseItem.superclass.render.call(this, container);
20186         this.container.menuItemId = this.id;
20187     },
20188
20189     // private
20190     onRender : function(container, position){
20191         this.el = Roo.get(this.el);
20192         container.dom.appendChild(this.el.dom);
20193     },
20194
20195     // private
20196     onClick : function(e){
20197         if(!this.disabled && this.fireEvent("click", this, e) !== false
20198                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20199             this.handleClick(e);
20200         }else{
20201             e.stopEvent();
20202         }
20203     },
20204
20205     // private
20206     activate : function(){
20207         if(this.disabled){
20208             return false;
20209         }
20210         var li = this.container;
20211         li.addClass(this.activeClass);
20212         this.region = li.getRegion().adjust(2, 2, -2, -2);
20213         this.fireEvent("activate", this);
20214         return true;
20215     },
20216
20217     // private
20218     deactivate : function(){
20219         this.container.removeClass(this.activeClass);
20220         this.fireEvent("deactivate", this);
20221     },
20222
20223     // private
20224     shouldDeactivate : function(e){
20225         return !this.region || !this.region.contains(e.getPoint());
20226     },
20227
20228     // private
20229     handleClick : function(e){
20230         if(this.hideOnClick){
20231             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20232         }
20233     },
20234
20235     // private
20236     expandMenu : function(autoActivate){
20237         // do nothing
20238     },
20239
20240     // private
20241     hideMenu : function(){
20242         // do nothing
20243     }
20244 });/*
20245  * Based on:
20246  * Ext JS Library 1.1.1
20247  * Copyright(c) 2006-2007, Ext JS, LLC.
20248  *
20249  * Originally Released Under LGPL - original licence link has changed is not relivant.
20250  *
20251  * Fork - LGPL
20252  * <script type="text/javascript">
20253  */
20254  
20255 /**
20256  * @class Roo.menu.Adapter
20257  * @extends Roo.menu.BaseItem
20258  * 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.
20259  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20260  * @constructor
20261  * Creates a new Adapter
20262  * @param {Object} config Configuration options
20263  */
20264 Roo.menu.Adapter = function(component, config){
20265     Roo.menu.Adapter.superclass.constructor.call(this, config);
20266     this.component = component;
20267 };
20268 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20269     // private
20270     canActivate : true,
20271
20272     // private
20273     onRender : function(container, position){
20274         this.component.render(container);
20275         this.el = this.component.getEl();
20276     },
20277
20278     // private
20279     activate : function(){
20280         if(this.disabled){
20281             return false;
20282         }
20283         this.component.focus();
20284         this.fireEvent("activate", this);
20285         return true;
20286     },
20287
20288     // private
20289     deactivate : function(){
20290         this.fireEvent("deactivate", this);
20291     },
20292
20293     // private
20294     disable : function(){
20295         this.component.disable();
20296         Roo.menu.Adapter.superclass.disable.call(this);
20297     },
20298
20299     // private
20300     enable : function(){
20301         this.component.enable();
20302         Roo.menu.Adapter.superclass.enable.call(this);
20303     }
20304 });/*
20305  * Based on:
20306  * Ext JS Library 1.1.1
20307  * Copyright(c) 2006-2007, Ext JS, LLC.
20308  *
20309  * Originally Released Under LGPL - original licence link has changed is not relivant.
20310  *
20311  * Fork - LGPL
20312  * <script type="text/javascript">
20313  */
20314
20315 /**
20316  * @class Roo.menu.TextItem
20317  * @extends Roo.menu.BaseItem
20318  * Adds a static text string to a menu, usually used as either a heading or group separator.
20319  * Note: old style constructor with text is still supported.
20320  * 
20321  * @constructor
20322  * Creates a new TextItem
20323  * @param {Object} cfg Configuration
20324  */
20325 Roo.menu.TextItem = function(cfg){
20326     if (typeof(cfg) == 'string') {
20327         this.text = cfg;
20328     } else {
20329         Roo.apply(this,cfg);
20330     }
20331     
20332     Roo.menu.TextItem.superclass.constructor.call(this);
20333 };
20334
20335 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20336     /**
20337      * @cfg {Boolean} text Text to show on item.
20338      */
20339     text : '',
20340     
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      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20347      */
20348     itemCls : "x-menu-text",
20349
20350     // private
20351     onRender : function(){
20352         var s = document.createElement("span");
20353         s.className = this.itemCls;
20354         s.innerHTML = this.text;
20355         this.el = s;
20356         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20357     }
20358 });/*
20359  * Based on:
20360  * Ext JS Library 1.1.1
20361  * Copyright(c) 2006-2007, Ext JS, LLC.
20362  *
20363  * Originally Released Under LGPL - original licence link has changed is not relivant.
20364  *
20365  * Fork - LGPL
20366  * <script type="text/javascript">
20367  */
20368
20369 /**
20370  * @class Roo.menu.Separator
20371  * @extends Roo.menu.BaseItem
20372  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20373  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20374  * @constructor
20375  * @param {Object} config Configuration options
20376  */
20377 Roo.menu.Separator = function(config){
20378     Roo.menu.Separator.superclass.constructor.call(this, config);
20379 };
20380
20381 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20382     /**
20383      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20384      */
20385     itemCls : "x-menu-sep",
20386     /**
20387      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20388      */
20389     hideOnClick : false,
20390
20391     // private
20392     onRender : function(li){
20393         var s = document.createElement("span");
20394         s.className = this.itemCls;
20395         s.innerHTML = "&#160;";
20396         this.el = s;
20397         li.addClass("x-menu-sep-li");
20398         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20399     }
20400 });/*
20401  * Based on:
20402  * Ext JS Library 1.1.1
20403  * Copyright(c) 2006-2007, Ext JS, LLC.
20404  *
20405  * Originally Released Under LGPL - original licence link has changed is not relivant.
20406  *
20407  * Fork - LGPL
20408  * <script type="text/javascript">
20409  */
20410 /**
20411  * @class Roo.menu.Item
20412  * @extends Roo.menu.BaseItem
20413  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20414  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20415  * activation and click handling.
20416  * @constructor
20417  * Creates a new Item
20418  * @param {Object} config Configuration options
20419  */
20420 Roo.menu.Item = function(config){
20421     Roo.menu.Item.superclass.constructor.call(this, config);
20422     if(this.menu){
20423         this.menu = Roo.menu.MenuMgr.get(this.menu);
20424     }
20425 };
20426 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20427     
20428     /**
20429      * @cfg {String} text
20430      * The text to show on the menu item.
20431      */
20432     text: '',
20433      /**
20434      * @cfg {String} HTML to render in menu
20435      * The text to show on the menu item (HTML version).
20436      */
20437     html: '',
20438     /**
20439      * @cfg {String} icon
20440      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20441      */
20442     icon: undefined,
20443     /**
20444      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20445      */
20446     itemCls : "x-menu-item",
20447     /**
20448      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20449      */
20450     canActivate : true,
20451     /**
20452      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20453      */
20454     showDelay: 200,
20455     // doc'd in BaseItem
20456     hideDelay: 200,
20457
20458     // private
20459     ctype: "Roo.menu.Item",
20460     
20461     // private
20462     onRender : function(container, position){
20463         var el = document.createElement("a");
20464         el.hideFocus = true;
20465         el.unselectable = "on";
20466         el.href = this.href || "#";
20467         if(this.hrefTarget){
20468             el.target = this.hrefTarget;
20469         }
20470         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20471         
20472         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20473         
20474         el.innerHTML = String.format(
20475                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20476                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20477         this.el = el;
20478         Roo.menu.Item.superclass.onRender.call(this, container, position);
20479     },
20480
20481     /**
20482      * Sets the text to display in this menu item
20483      * @param {String} text The text to display
20484      * @param {Boolean} isHTML true to indicate text is pure html.
20485      */
20486     setText : function(text, isHTML){
20487         if (isHTML) {
20488             this.html = text;
20489         } else {
20490             this.text = text;
20491             this.html = '';
20492         }
20493         if(this.rendered){
20494             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20495      
20496             this.el.update(String.format(
20497                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20498                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20499             this.parentMenu.autoWidth();
20500         }
20501     },
20502
20503     // private
20504     handleClick : function(e){
20505         if(!this.href){ // if no link defined, stop the event automatically
20506             e.stopEvent();
20507         }
20508         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20509     },
20510
20511     // private
20512     activate : function(autoExpand){
20513         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20514             this.focus();
20515             if(autoExpand){
20516                 this.expandMenu();
20517             }
20518         }
20519         return true;
20520     },
20521
20522     // private
20523     shouldDeactivate : function(e){
20524         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20525             if(this.menu && this.menu.isVisible()){
20526                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20527             }
20528             return true;
20529         }
20530         return false;
20531     },
20532
20533     // private
20534     deactivate : function(){
20535         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20536         this.hideMenu();
20537     },
20538
20539     // private
20540     expandMenu : function(autoActivate){
20541         if(!this.disabled && this.menu){
20542             clearTimeout(this.hideTimer);
20543             delete this.hideTimer;
20544             if(!this.menu.isVisible() && !this.showTimer){
20545                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20546             }else if (this.menu.isVisible() && autoActivate){
20547                 this.menu.tryActivate(0, 1);
20548             }
20549         }
20550     },
20551
20552     // private
20553     deferExpand : function(autoActivate){
20554         delete this.showTimer;
20555         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20556         if(autoActivate){
20557             this.menu.tryActivate(0, 1);
20558         }
20559     },
20560
20561     // private
20562     hideMenu : function(){
20563         clearTimeout(this.showTimer);
20564         delete this.showTimer;
20565         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20566             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20567         }
20568     },
20569
20570     // private
20571     deferHide : function(){
20572         delete this.hideTimer;
20573         this.menu.hide();
20574     }
20575 });/*
20576  * Based on:
20577  * Ext JS Library 1.1.1
20578  * Copyright(c) 2006-2007, Ext JS, LLC.
20579  *
20580  * Originally Released Under LGPL - original licence link has changed is not relivant.
20581  *
20582  * Fork - LGPL
20583  * <script type="text/javascript">
20584  */
20585  
20586 /**
20587  * @class Roo.menu.CheckItem
20588  * @extends Roo.menu.Item
20589  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20590  * @constructor
20591  * Creates a new CheckItem
20592  * @param {Object} config Configuration options
20593  */
20594 Roo.menu.CheckItem = function(config){
20595     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20596     this.addEvents({
20597         /**
20598          * @event beforecheckchange
20599          * Fires before the checked value is set, providing an opportunity to cancel if needed
20600          * @param {Roo.menu.CheckItem} this
20601          * @param {Boolean} checked The new checked value that will be set
20602          */
20603         "beforecheckchange" : true,
20604         /**
20605          * @event checkchange
20606          * Fires after the checked value has been set
20607          * @param {Roo.menu.CheckItem} this
20608          * @param {Boolean} checked The checked value that was set
20609          */
20610         "checkchange" : true
20611     });
20612     if(this.checkHandler){
20613         this.on('checkchange', this.checkHandler, this.scope);
20614     }
20615 };
20616 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20617     /**
20618      * @cfg {String} group
20619      * All check items with the same group name will automatically be grouped into a single-select
20620      * radio button group (defaults to '')
20621      */
20622     /**
20623      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20624      */
20625     itemCls : "x-menu-item x-menu-check-item",
20626     /**
20627      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20628      */
20629     groupClass : "x-menu-group-item",
20630
20631     /**
20632      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20633      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20634      * initialized with checked = true will be rendered as checked.
20635      */
20636     checked: false,
20637
20638     // private
20639     ctype: "Roo.menu.CheckItem",
20640
20641     // private
20642     onRender : function(c){
20643         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20644         if(this.group){
20645             this.el.addClass(this.groupClass);
20646         }
20647         Roo.menu.MenuMgr.registerCheckable(this);
20648         if(this.checked){
20649             this.checked = false;
20650             this.setChecked(true, true);
20651         }
20652     },
20653
20654     // private
20655     destroy : function(){
20656         if(this.rendered){
20657             Roo.menu.MenuMgr.unregisterCheckable(this);
20658         }
20659         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20660     },
20661
20662     /**
20663      * Set the checked state of this item
20664      * @param {Boolean} checked The new checked value
20665      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20666      */
20667     setChecked : function(state, suppressEvent){
20668         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20669             if(this.container){
20670                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20671             }
20672             this.checked = state;
20673             if(suppressEvent !== true){
20674                 this.fireEvent("checkchange", this, state);
20675             }
20676         }
20677     },
20678
20679     // private
20680     handleClick : function(e){
20681        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20682            this.setChecked(!this.checked);
20683        }
20684        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20685     }
20686 });/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696  
20697 /**
20698  * @class Roo.menu.DateItem
20699  * @extends Roo.menu.Adapter
20700  * A menu item that wraps the {@link Roo.DatPicker} component.
20701  * @constructor
20702  * Creates a new DateItem
20703  * @param {Object} config Configuration options
20704  */
20705 Roo.menu.DateItem = function(config){
20706     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20707     /** The Roo.DatePicker object @type Roo.DatePicker */
20708     this.picker = this.component;
20709     this.addEvents({select: true});
20710     
20711     this.picker.on("render", function(picker){
20712         picker.getEl().swallowEvent("click");
20713         picker.container.addClass("x-menu-date-item");
20714     });
20715
20716     this.picker.on("select", this.onSelect, this);
20717 };
20718
20719 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20720     // private
20721     onSelect : function(picker, date){
20722         this.fireEvent("select", this, date, picker);
20723         Roo.menu.DateItem.superclass.handleClick.call(this);
20724     }
20725 });/*
20726  * Based on:
20727  * Ext JS Library 1.1.1
20728  * Copyright(c) 2006-2007, Ext JS, LLC.
20729  *
20730  * Originally Released Under LGPL - original licence link has changed is not relivant.
20731  *
20732  * Fork - LGPL
20733  * <script type="text/javascript">
20734  */
20735  
20736 /**
20737  * @class Roo.menu.ColorItem
20738  * @extends Roo.menu.Adapter
20739  * A menu item that wraps the {@link Roo.ColorPalette} component.
20740  * @constructor
20741  * Creates a new ColorItem
20742  * @param {Object} config Configuration options
20743  */
20744 Roo.menu.ColorItem = function(config){
20745     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20746     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20747     this.palette = this.component;
20748     this.relayEvents(this.palette, ["select"]);
20749     if(this.selectHandler){
20750         this.on('select', this.selectHandler, this.scope);
20751     }
20752 };
20753 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
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.DateMenu
20767  * @extends Roo.menu.Menu
20768  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20769  * @constructor
20770  * Creates a new DateMenu
20771  * @param {Object} config Configuration options
20772  */
20773 Roo.menu.DateMenu = function(config){
20774     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20775     this.plain = true;
20776     var di = new Roo.menu.DateItem(config);
20777     this.add(di);
20778     /**
20779      * The {@link Roo.DatePicker} instance for this DateMenu
20780      * @type DatePicker
20781      */
20782     this.picker = di.picker;
20783     /**
20784      * @event select
20785      * @param {DatePicker} picker
20786      * @param {Date} date
20787      */
20788     this.relayEvents(di, ["select"]);
20789
20790     this.on('beforeshow', function(){
20791         if(this.picker){
20792             this.picker.hideMonthPicker(true);
20793         }
20794     }, this);
20795 };
20796 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20797     cls:'x-date-menu'
20798 });/*
20799  * Based on:
20800  * Ext JS Library 1.1.1
20801  * Copyright(c) 2006-2007, Ext JS, LLC.
20802  *
20803  * Originally Released Under LGPL - original licence link has changed is not relivant.
20804  *
20805  * Fork - LGPL
20806  * <script type="text/javascript">
20807  */
20808  
20809
20810 /**
20811  * @class Roo.menu.ColorMenu
20812  * @extends Roo.menu.Menu
20813  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20814  * @constructor
20815  * Creates a new ColorMenu
20816  * @param {Object} config Configuration options
20817  */
20818 Roo.menu.ColorMenu = function(config){
20819     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20820     this.plain = true;
20821     var ci = new Roo.menu.ColorItem(config);
20822     this.add(ci);
20823     /**
20824      * The {@link Roo.ColorPalette} instance for this ColorMenu
20825      * @type ColorPalette
20826      */
20827     this.palette = ci.palette;
20828     /**
20829      * @event select
20830      * @param {ColorPalette} palette
20831      * @param {String} color
20832      */
20833     this.relayEvents(ci, ["select"]);
20834 };
20835 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20836  * Based on:
20837  * Ext JS Library 1.1.1
20838  * Copyright(c) 2006-2007, Ext JS, LLC.
20839  *
20840  * Originally Released Under LGPL - original licence link has changed is not relivant.
20841  *
20842  * Fork - LGPL
20843  * <script type="text/javascript">
20844  */
20845  
20846 /**
20847  * @class Roo.form.Field
20848  * @extends Roo.BoxComponent
20849  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20850  * @constructor
20851  * Creates a new Field
20852  * @param {Object} config Configuration options
20853  */
20854 Roo.form.Field = function(config){
20855     Roo.form.Field.superclass.constructor.call(this, config);
20856 };
20857
20858 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20859     /**
20860      * @cfg {String} fieldLabel Label to use when rendering a form.
20861      */
20862        /**
20863      * @cfg {String} qtip Mouse over tip
20864      */
20865      
20866     /**
20867      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20868      */
20869     invalidClass : "x-form-invalid",
20870     /**
20871      * @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")
20872      */
20873     invalidText : "The value in this field is invalid",
20874     /**
20875      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20876      */
20877     focusClass : "x-form-focus",
20878     /**
20879      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20880       automatic validation (defaults to "keyup").
20881      */
20882     validationEvent : "keyup",
20883     /**
20884      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20885      */
20886     validateOnBlur : true,
20887     /**
20888      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20889      */
20890     validationDelay : 250,
20891     /**
20892      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20893      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20894      */
20895     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20896     /**
20897      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20898      */
20899     fieldClass : "x-form-field",
20900     /**
20901      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20902      *<pre>
20903 Value         Description
20904 -----------   ----------------------------------------------------------------------
20905 qtip          Display a quick tip when the user hovers over the field
20906 title         Display a default browser title attribute popup
20907 under         Add a block div beneath the field containing the error text
20908 side          Add an error icon to the right of the field with a popup on hover
20909 [element id]  Add the error text directly to the innerHTML of the specified element
20910 </pre>
20911      */
20912     msgTarget : 'qtip',
20913     /**
20914      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20915      */
20916     msgFx : 'normal',
20917
20918     /**
20919      * @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.
20920      */
20921     readOnly : false,
20922
20923     /**
20924      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20925      */
20926     disabled : false,
20927
20928     /**
20929      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20930      */
20931     inputType : undefined,
20932     
20933     /**
20934      * @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).
20935          */
20936         tabIndex : undefined,
20937         
20938     // private
20939     isFormField : true,
20940
20941     // private
20942     hasFocus : false,
20943     /**
20944      * @property {Roo.Element} fieldEl
20945      * Element Containing the rendered Field (with label etc.)
20946      */
20947     /**
20948      * @cfg {Mixed} value A value to initialize this field with.
20949      */
20950     value : undefined,
20951
20952     /**
20953      * @cfg {String} name The field's HTML name attribute.
20954      */
20955     /**
20956      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20957      */
20958
20959         // private ??
20960         initComponent : function(){
20961         Roo.form.Field.superclass.initComponent.call(this);
20962         this.addEvents({
20963             /**
20964              * @event focus
20965              * Fires when this field receives input focus.
20966              * @param {Roo.form.Field} this
20967              */
20968             focus : true,
20969             /**
20970              * @event blur
20971              * Fires when this field loses input focus.
20972              * @param {Roo.form.Field} this
20973              */
20974             blur : true,
20975             /**
20976              * @event specialkey
20977              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20978              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20979              * @param {Roo.form.Field} this
20980              * @param {Roo.EventObject} e The event object
20981              */
20982             specialkey : true,
20983             /**
20984              * @event change
20985              * Fires just before the field blurs if the field value has changed.
20986              * @param {Roo.form.Field} this
20987              * @param {Mixed} newValue The new value
20988              * @param {Mixed} oldValue The original value
20989              */
20990             change : true,
20991             /**
20992              * @event invalid
20993              * Fires after the field has been marked as invalid.
20994              * @param {Roo.form.Field} this
20995              * @param {String} msg The validation message
20996              */
20997             invalid : true,
20998             /**
20999              * @event valid
21000              * Fires after the field has been validated with no errors.
21001              * @param {Roo.form.Field} this
21002              */
21003             valid : true,
21004              /**
21005              * @event keyup
21006              * Fires after the key up
21007              * @param {Roo.form.Field} this
21008              * @param {Roo.EventObject}  e The event Object
21009              */
21010             keyup : true
21011         });
21012     },
21013
21014     /**
21015      * Returns the name attribute of the field if available
21016      * @return {String} name The field name
21017      */
21018     getName: function(){
21019          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21020     },
21021
21022     // private
21023     onRender : function(ct, position){
21024         Roo.form.Field.superclass.onRender.call(this, ct, position);
21025         if(!this.el){
21026             var cfg = this.getAutoCreate();
21027             if(!cfg.name){
21028                 cfg.name = this.name || this.id;
21029             }
21030             if(this.inputType){
21031                 cfg.type = this.inputType;
21032             }
21033             this.el = ct.createChild(cfg, position);
21034         }
21035         var type = this.el.dom.type;
21036         if(type){
21037             if(type == 'password'){
21038                 type = 'text';
21039             }
21040             this.el.addClass('x-form-'+type);
21041         }
21042         if(this.readOnly){
21043             this.el.dom.readOnly = true;
21044         }
21045         if(this.tabIndex !== undefined){
21046             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21047         }
21048
21049         this.el.addClass([this.fieldClass, this.cls]);
21050         this.initValue();
21051     },
21052
21053     /**
21054      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21055      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21056      * @return {Roo.form.Field} this
21057      */
21058     applyTo : function(target){
21059         this.allowDomMove = false;
21060         this.el = Roo.get(target);
21061         this.render(this.el.dom.parentNode);
21062         return this;
21063     },
21064
21065     // private
21066     initValue : function(){
21067         if(this.value !== undefined){
21068             this.setValue(this.value);
21069         }else if(this.el.dom.value.length > 0){
21070             this.setValue(this.el.dom.value);
21071         }
21072     },
21073
21074     /**
21075      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21076      */
21077     isDirty : function() {
21078         if(this.disabled) {
21079             return false;
21080         }
21081         return String(this.getValue()) !== String(this.originalValue);
21082     },
21083
21084     // private
21085     afterRender : function(){
21086         Roo.form.Field.superclass.afterRender.call(this);
21087         this.initEvents();
21088     },
21089
21090     // private
21091     fireKey : function(e){
21092         //Roo.log('field ' + e.getKey());
21093         if(e.isNavKeyPress()){
21094             this.fireEvent("specialkey", this, e);
21095         }
21096     },
21097
21098     /**
21099      * Resets the current field value to the originally loaded value and clears any validation messages
21100      */
21101     reset : function(){
21102         this.setValue(this.originalValue);
21103         this.clearInvalid();
21104     },
21105
21106     // private
21107     initEvents : function(){
21108         // safari killled keypress - so keydown is now used..
21109         this.el.on("keydown" , this.fireKey,  this);
21110         this.el.on("focus", this.onFocus,  this);
21111         this.el.on("blur", this.onBlur,  this);
21112         this.el.relayEvent('keyup', this);
21113
21114         // reference to original value for reset
21115         this.originalValue = this.getValue();
21116     },
21117
21118     // private
21119     onFocus : function(){
21120         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21121             this.el.addClass(this.focusClass);
21122         }
21123         if(!this.hasFocus){
21124             this.hasFocus = true;
21125             this.startValue = this.getValue();
21126             this.fireEvent("focus", this);
21127         }
21128     },
21129
21130     beforeBlur : Roo.emptyFn,
21131
21132     // private
21133     onBlur : function(){
21134         this.beforeBlur();
21135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21136             this.el.removeClass(this.focusClass);
21137         }
21138         this.hasFocus = false;
21139         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21140             this.validate();
21141         }
21142         var v = this.getValue();
21143         if(String(v) !== String(this.startValue)){
21144             this.fireEvent('change', this, v, this.startValue);
21145         }
21146         this.fireEvent("blur", this);
21147     },
21148
21149     /**
21150      * Returns whether or not the field value is currently valid
21151      * @param {Boolean} preventMark True to disable marking the field invalid
21152      * @return {Boolean} True if the value is valid, else false
21153      */
21154     isValid : function(preventMark){
21155         if(this.disabled){
21156             return true;
21157         }
21158         var restore = this.preventMark;
21159         this.preventMark = preventMark === true;
21160         var v = this.validateValue(this.processValue(this.getRawValue()));
21161         this.preventMark = restore;
21162         return v;
21163     },
21164
21165     /**
21166      * Validates the field value
21167      * @return {Boolean} True if the value is valid, else false
21168      */
21169     validate : function(){
21170         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21171             this.clearInvalid();
21172             return true;
21173         }
21174         return false;
21175     },
21176
21177     processValue : function(value){
21178         return value;
21179     },
21180
21181     // private
21182     // Subclasses should provide the validation implementation by overriding this
21183     validateValue : function(value){
21184         return true;
21185     },
21186
21187     /**
21188      * Mark this field as invalid
21189      * @param {String} msg The validation message
21190      */
21191     markInvalid : function(msg){
21192         if(!this.rendered || this.preventMark){ // not rendered
21193             return;
21194         }
21195         this.el.addClass(this.invalidClass);
21196         msg = msg || this.invalidText;
21197         switch(this.msgTarget){
21198             case 'qtip':
21199                 this.el.dom.qtip = msg;
21200                 this.el.dom.qclass = 'x-form-invalid-tip';
21201                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21202                     Roo.QuickTips.enable();
21203                 }
21204                 break;
21205             case 'title':
21206                 this.el.dom.title = msg;
21207                 break;
21208             case 'under':
21209                 if(!this.errorEl){
21210                     var elp = this.el.findParent('.x-form-element', 5, true);
21211                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21212                     this.errorEl.setWidth(elp.getWidth(true)-20);
21213                 }
21214                 this.errorEl.update(msg);
21215                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21216                 break;
21217             case 'side':
21218                 if(!this.errorIcon){
21219                     var elp = this.el.findParent('.x-form-element', 5, true);
21220                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21221                 }
21222                 this.alignErrorIcon();
21223                 this.errorIcon.dom.qtip = msg;
21224                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21225                 this.errorIcon.show();
21226                 this.on('resize', this.alignErrorIcon, this);
21227                 break;
21228             default:
21229                 var t = Roo.getDom(this.msgTarget);
21230                 t.innerHTML = msg;
21231                 t.style.display = this.msgDisplay;
21232                 break;
21233         }
21234         this.fireEvent('invalid', this, msg);
21235     },
21236
21237     // private
21238     alignErrorIcon : function(){
21239         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21240     },
21241
21242     /**
21243      * Clear any invalid styles/messages for this field
21244      */
21245     clearInvalid : function(){
21246         if(!this.rendered || this.preventMark){ // not rendered
21247             return;
21248         }
21249         this.el.removeClass(this.invalidClass);
21250         switch(this.msgTarget){
21251             case 'qtip':
21252                 this.el.dom.qtip = '';
21253                 break;
21254             case 'title':
21255                 this.el.dom.title = '';
21256                 break;
21257             case 'under':
21258                 if(this.errorEl){
21259                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21260                 }
21261                 break;
21262             case 'side':
21263                 if(this.errorIcon){
21264                     this.errorIcon.dom.qtip = '';
21265                     this.errorIcon.hide();
21266                     this.un('resize', this.alignErrorIcon, this);
21267                 }
21268                 break;
21269             default:
21270                 var t = Roo.getDom(this.msgTarget);
21271                 t.innerHTML = '';
21272                 t.style.display = 'none';
21273                 break;
21274         }
21275         this.fireEvent('valid', this);
21276     },
21277
21278     /**
21279      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21280      * @return {Mixed} value The field value
21281      */
21282     getRawValue : function(){
21283         var v = this.el.getValue();
21284         if(v === this.emptyText){
21285             v = '';
21286         }
21287         return v;
21288     },
21289
21290     /**
21291      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21292      * @return {Mixed} value The field value
21293      */
21294     getValue : function(){
21295         var v = this.el.getValue();
21296         if(v === this.emptyText || v === undefined){
21297             v = '';
21298         }
21299         return v;
21300     },
21301
21302     /**
21303      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21304      * @param {Mixed} value The value to set
21305      */
21306     setRawValue : function(v){
21307         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21308     },
21309
21310     /**
21311      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21312      * @param {Mixed} value The value to set
21313      */
21314     setValue : function(v){
21315         this.value = v;
21316         if(this.rendered){
21317             this.el.dom.value = (v === null || v === undefined ? '' : v);
21318             this.validate();
21319         }
21320     },
21321
21322     adjustSize : function(w, h){
21323         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21324         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21325         return s;
21326     },
21327
21328     adjustWidth : function(tag, w){
21329         tag = tag.toLowerCase();
21330         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21331             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21332                 if(tag == 'input'){
21333                     return w + 2;
21334                 }
21335                 if(tag = 'textarea'){
21336                     return w-2;
21337                 }
21338             }else if(Roo.isOpera){
21339                 if(tag == 'input'){
21340                     return w + 2;
21341                 }
21342                 if(tag = 'textarea'){
21343                     return w-2;
21344                 }
21345             }
21346         }
21347         return w;
21348     }
21349 });
21350
21351
21352 // anything other than normal should be considered experimental
21353 Roo.form.Field.msgFx = {
21354     normal : {
21355         show: function(msgEl, f){
21356             msgEl.setDisplayed('block');
21357         },
21358
21359         hide : function(msgEl, f){
21360             msgEl.setDisplayed(false).update('');
21361         }
21362     },
21363
21364     slide : {
21365         show: function(msgEl, f){
21366             msgEl.slideIn('t', {stopFx:true});
21367         },
21368
21369         hide : function(msgEl, f){
21370             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21371         }
21372     },
21373
21374     slideRight : {
21375         show: function(msgEl, f){
21376             msgEl.fixDisplay();
21377             msgEl.alignTo(f.el, 'tl-tr');
21378             msgEl.slideIn('l', {stopFx:true});
21379         },
21380
21381         hide : function(msgEl, f){
21382             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21383         }
21384     }
21385 };/*
21386  * Based on:
21387  * Ext JS Library 1.1.1
21388  * Copyright(c) 2006-2007, Ext JS, LLC.
21389  *
21390  * Originally Released Under LGPL - original licence link has changed is not relivant.
21391  *
21392  * Fork - LGPL
21393  * <script type="text/javascript">
21394  */
21395  
21396
21397 /**
21398  * @class Roo.form.TextField
21399  * @extends Roo.form.Field
21400  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21401  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21402  * @constructor
21403  * Creates a new TextField
21404  * @param {Object} config Configuration options
21405  */
21406 Roo.form.TextField = function(config){
21407     Roo.form.TextField.superclass.constructor.call(this, config);
21408     this.addEvents({
21409         /**
21410          * @event autosize
21411          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21412          * according to the default logic, but this event provides a hook for the developer to apply additional
21413          * logic at runtime to resize the field if needed.
21414              * @param {Roo.form.Field} this This text field
21415              * @param {Number} width The new field width
21416              */
21417         autosize : true
21418     });
21419 };
21420
21421 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21422     /**
21423      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21424      */
21425     grow : false,
21426     /**
21427      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21428      */
21429     growMin : 30,
21430     /**
21431      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21432      */
21433     growMax : 800,
21434     /**
21435      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21436      */
21437     vtype : null,
21438     /**
21439      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21440      */
21441     maskRe : null,
21442     /**
21443      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21444      */
21445     disableKeyFilter : false,
21446     /**
21447      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21448      */
21449     allowBlank : true,
21450     /**
21451      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21452      */
21453     minLength : 0,
21454     /**
21455      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21456      */
21457     maxLength : Number.MAX_VALUE,
21458     /**
21459      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21460      */
21461     minLengthText : "The minimum length for this field is {0}",
21462     /**
21463      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21464      */
21465     maxLengthText : "The maximum length for this field is {0}",
21466     /**
21467      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21468      */
21469     selectOnFocus : false,
21470     /**
21471      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21472      */
21473     blankText : "This field is required",
21474     /**
21475      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21476      * If available, this function will be called only after the basic validators all return true, and will be passed the
21477      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21478      */
21479     validator : null,
21480     /**
21481      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21482      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21483      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21484      */
21485     regex : null,
21486     /**
21487      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21488      */
21489     regexText : "",
21490     /**
21491      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21492      */
21493     emptyText : null,
21494     /**
21495      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21496      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21497      */
21498     emptyClass : 'x-form-empty-field',
21499
21500     // private
21501     initEvents : function(){
21502         Roo.form.TextField.superclass.initEvents.call(this);
21503         if(this.validationEvent == 'keyup'){
21504             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21505             this.el.on('keyup', this.filterValidation, this);
21506         }
21507         else if(this.validationEvent !== false){
21508             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21509         }
21510         if(this.selectOnFocus || this.emptyText){
21511             this.on("focus", this.preFocus, this);
21512             if(this.emptyText){
21513                 this.on('blur', this.postBlur, this);
21514                 this.applyEmptyText();
21515             }
21516         }
21517         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21518             this.el.on("keypress", this.filterKeys, this);
21519         }
21520         if(this.grow){
21521             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21522             this.el.on("click", this.autoSize,  this);
21523         }
21524     },
21525
21526     processValue : function(value){
21527         if(this.stripCharsRe){
21528             var newValue = value.replace(this.stripCharsRe, '');
21529             if(newValue !== value){
21530                 this.setRawValue(newValue);
21531                 return newValue;
21532             }
21533         }
21534         return value;
21535     },
21536
21537     filterValidation : function(e){
21538         if(!e.isNavKeyPress()){
21539             this.validationTask.delay(this.validationDelay);
21540         }
21541     },
21542
21543     // private
21544     onKeyUp : function(e){
21545         if(!e.isNavKeyPress()){
21546             this.autoSize();
21547         }
21548     },
21549
21550     /**
21551      * Resets the current field value to the originally-loaded value and clears any validation messages.
21552      * Also adds emptyText and emptyClass if the original value was blank.
21553      */
21554     reset : function(){
21555         Roo.form.TextField.superclass.reset.call(this);
21556         this.applyEmptyText();
21557     },
21558
21559     applyEmptyText : function(){
21560         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21561             this.setRawValue(this.emptyText);
21562             this.el.addClass(this.emptyClass);
21563         }
21564     },
21565
21566     // private
21567     preFocus : function(){
21568         if(this.emptyText){
21569             if(this.el.dom.value == this.emptyText){
21570                 this.setRawValue('');
21571             }
21572             this.el.removeClass(this.emptyClass);
21573         }
21574         if(this.selectOnFocus){
21575             this.el.dom.select();
21576         }
21577     },
21578
21579     // private
21580     postBlur : function(){
21581         this.applyEmptyText();
21582     },
21583
21584     // private
21585     filterKeys : function(e){
21586         var k = e.getKey();
21587         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21588             return;
21589         }
21590         var c = e.getCharCode(), cc = String.fromCharCode(c);
21591         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21592             return;
21593         }
21594         if(!this.maskRe.test(cc)){
21595             e.stopEvent();
21596         }
21597     },
21598
21599     setValue : function(v){
21600         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21601             this.el.removeClass(this.emptyClass);
21602         }
21603         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21604         this.applyEmptyText();
21605         this.autoSize();
21606     },
21607
21608     /**
21609      * Validates a value according to the field's validation rules and marks the field as invalid
21610      * if the validation fails
21611      * @param {Mixed} value The value to validate
21612      * @return {Boolean} True if the value is valid, else false
21613      */
21614     validateValue : function(value){
21615         if(value.length < 1 || value === this.emptyText){ // if it's blank
21616              if(this.allowBlank){
21617                 this.clearInvalid();
21618                 return true;
21619              }else{
21620                 this.markInvalid(this.blankText);
21621                 return false;
21622              }
21623         }
21624         if(value.length < this.minLength){
21625             this.markInvalid(String.format(this.minLengthText, this.minLength));
21626             return false;
21627         }
21628         if(value.length > this.maxLength){
21629             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21630             return false;
21631         }
21632         if(this.vtype){
21633             var vt = Roo.form.VTypes;
21634             if(!vt[this.vtype](value, this)){
21635                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21636                 return false;
21637             }
21638         }
21639         if(typeof this.validator == "function"){
21640             var msg = this.validator(value);
21641             if(msg !== true){
21642                 this.markInvalid(msg);
21643                 return false;
21644             }
21645         }
21646         if(this.regex && !this.regex.test(value)){
21647             this.markInvalid(this.regexText);
21648             return false;
21649         }
21650         return true;
21651     },
21652
21653     /**
21654      * Selects text in this field
21655      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21656      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21657      */
21658     selectText : function(start, end){
21659         var v = this.getRawValue();
21660         if(v.length > 0){
21661             start = start === undefined ? 0 : start;
21662             end = end === undefined ? v.length : end;
21663             var d = this.el.dom;
21664             if(d.setSelectionRange){
21665                 d.setSelectionRange(start, end);
21666             }else if(d.createTextRange){
21667                 var range = d.createTextRange();
21668                 range.moveStart("character", start);
21669                 range.moveEnd("character", v.length-end);
21670                 range.select();
21671             }
21672         }
21673     },
21674
21675     /**
21676      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21677      * This only takes effect if grow = true, and fires the autosize event.
21678      */
21679     autoSize : function(){
21680         if(!this.grow || !this.rendered){
21681             return;
21682         }
21683         if(!this.metrics){
21684             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21685         }
21686         var el = this.el;
21687         var v = el.dom.value;
21688         var d = document.createElement('div');
21689         d.appendChild(document.createTextNode(v));
21690         v = d.innerHTML;
21691         d = null;
21692         v += "&#160;";
21693         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21694         this.el.setWidth(w);
21695         this.fireEvent("autosize", this, w);
21696     }
21697 });/*
21698  * Based on:
21699  * Ext JS Library 1.1.1
21700  * Copyright(c) 2006-2007, Ext JS, LLC.
21701  *
21702  * Originally Released Under LGPL - original licence link has changed is not relivant.
21703  *
21704  * Fork - LGPL
21705  * <script type="text/javascript">
21706  */
21707  
21708 /**
21709  * @class Roo.form.Hidden
21710  * @extends Roo.form.TextField
21711  * Simple Hidden element used on forms 
21712  * 
21713  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21714  * 
21715  * @constructor
21716  * Creates a new Hidden form element.
21717  * @param {Object} config Configuration options
21718  */
21719
21720
21721
21722 // easy hidden field...
21723 Roo.form.Hidden = function(config){
21724     Roo.form.Hidden.superclass.constructor.call(this, config);
21725 };
21726   
21727 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21728     fieldLabel:      '',
21729     inputType:      'hidden',
21730     width:          50,
21731     allowBlank:     true,
21732     labelSeparator: '',
21733     hidden:         true,
21734     itemCls :       'x-form-item-display-none'
21735
21736
21737 });
21738
21739
21740 /*
21741  * Based on:
21742  * Ext JS Library 1.1.1
21743  * Copyright(c) 2006-2007, Ext JS, LLC.
21744  *
21745  * Originally Released Under LGPL - original licence link has changed is not relivant.
21746  *
21747  * Fork - LGPL
21748  * <script type="text/javascript">
21749  */
21750  
21751 /**
21752  * @class Roo.form.TriggerField
21753  * @extends Roo.form.TextField
21754  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21755  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21756  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21757  * for which you can provide a custom implementation.  For example:
21758  * <pre><code>
21759 var trigger = new Roo.form.TriggerField();
21760 trigger.onTriggerClick = myTriggerFn;
21761 trigger.applyTo('my-field');
21762 </code></pre>
21763  *
21764  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21765  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21766  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21767  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21768  * @constructor
21769  * Create a new TriggerField.
21770  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21771  * to the base TextField)
21772  */
21773 Roo.form.TriggerField = function(config){
21774     this.mimicing = false;
21775     Roo.form.TriggerField.superclass.constructor.call(this, config);
21776 };
21777
21778 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21779     /**
21780      * @cfg {String} triggerClass A CSS class to apply to the trigger
21781      */
21782     /**
21783      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21784      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21785      */
21786     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21787     /**
21788      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21789      */
21790     hideTrigger:false,
21791
21792     /** @cfg {Boolean} grow @hide */
21793     /** @cfg {Number} growMin @hide */
21794     /** @cfg {Number} growMax @hide */
21795
21796     /**
21797      * @hide 
21798      * @method
21799      */
21800     autoSize: Roo.emptyFn,
21801     // private
21802     monitorTab : true,
21803     // private
21804     deferHeight : true,
21805
21806     
21807     actionMode : 'wrap',
21808     // private
21809     onResize : function(w, h){
21810         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21811         if(typeof w == 'number'){
21812             var x = w - this.trigger.getWidth();
21813             this.el.setWidth(this.adjustWidth('input', x));
21814             this.trigger.setStyle('left', x+'px');
21815         }
21816     },
21817
21818     // private
21819     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21820
21821     // private
21822     getResizeEl : function(){
21823         return this.wrap;
21824     },
21825
21826     // private
21827     getPositionEl : function(){
21828         return this.wrap;
21829     },
21830
21831     // private
21832     alignErrorIcon : function(){
21833         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21834     },
21835
21836     // private
21837     onRender : function(ct, position){
21838         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21839         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21840         this.trigger = this.wrap.createChild(this.triggerConfig ||
21841                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21842         if(this.hideTrigger){
21843             this.trigger.setDisplayed(false);
21844         }
21845         this.initTrigger();
21846         if(!this.width){
21847             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21848         }
21849     },
21850
21851     // private
21852     initTrigger : function(){
21853         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21854         this.trigger.addClassOnOver('x-form-trigger-over');
21855         this.trigger.addClassOnClick('x-form-trigger-click');
21856     },
21857
21858     // private
21859     onDestroy : function(){
21860         if(this.trigger){
21861             this.trigger.removeAllListeners();
21862             this.trigger.remove();
21863         }
21864         if(this.wrap){
21865             this.wrap.remove();
21866         }
21867         Roo.form.TriggerField.superclass.onDestroy.call(this);
21868     },
21869
21870     // private
21871     onFocus : function(){
21872         Roo.form.TriggerField.superclass.onFocus.call(this);
21873         if(!this.mimicing){
21874             this.wrap.addClass('x-trigger-wrap-focus');
21875             this.mimicing = true;
21876             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21877             if(this.monitorTab){
21878                 this.el.on("keydown", this.checkTab, this);
21879             }
21880         }
21881     },
21882
21883     // private
21884     checkTab : function(e){
21885         if(e.getKey() == e.TAB){
21886             this.triggerBlur();
21887         }
21888     },
21889
21890     // private
21891     onBlur : function(){
21892         // do nothing
21893     },
21894
21895     // private
21896     mimicBlur : function(e, t){
21897         if(!this.wrap.contains(t) && this.validateBlur()){
21898             this.triggerBlur();
21899         }
21900     },
21901
21902     // private
21903     triggerBlur : function(){
21904         this.mimicing = false;
21905         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21906         if(this.monitorTab){
21907             this.el.un("keydown", this.checkTab, this);
21908         }
21909         this.wrap.removeClass('x-trigger-wrap-focus');
21910         Roo.form.TriggerField.superclass.onBlur.call(this);
21911     },
21912
21913     // private
21914     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21915     validateBlur : function(e, t){
21916         return true;
21917     },
21918
21919     // private
21920     onDisable : function(){
21921         Roo.form.TriggerField.superclass.onDisable.call(this);
21922         if(this.wrap){
21923             this.wrap.addClass('x-item-disabled');
21924         }
21925     },
21926
21927     // private
21928     onEnable : function(){
21929         Roo.form.TriggerField.superclass.onEnable.call(this);
21930         if(this.wrap){
21931             this.wrap.removeClass('x-item-disabled');
21932         }
21933     },
21934
21935     // private
21936     onShow : function(){
21937         var ae = this.getActionEl();
21938         
21939         if(ae){
21940             ae.dom.style.display = '';
21941             ae.dom.style.visibility = 'visible';
21942         }
21943     },
21944
21945     // private
21946     
21947     onHide : function(){
21948         var ae = this.getActionEl();
21949         ae.dom.style.display = 'none';
21950     },
21951
21952     /**
21953      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21954      * by an implementing function.
21955      * @method
21956      * @param {EventObject} e
21957      */
21958     onTriggerClick : Roo.emptyFn
21959 });
21960
21961 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21962 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21963 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21964 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21965     initComponent : function(){
21966         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21967
21968         this.triggerConfig = {
21969             tag:'span', cls:'x-form-twin-triggers', cn:[
21970             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21971             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21972         ]};
21973     },
21974
21975     getTrigger : function(index){
21976         return this.triggers[index];
21977     },
21978
21979     initTrigger : function(){
21980         var ts = this.trigger.select('.x-form-trigger', true);
21981         this.wrap.setStyle('overflow', 'hidden');
21982         var triggerField = this;
21983         ts.each(function(t, all, index){
21984             t.hide = function(){
21985                 var w = triggerField.wrap.getWidth();
21986                 this.dom.style.display = 'none';
21987                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21988             };
21989             t.show = function(){
21990                 var w = triggerField.wrap.getWidth();
21991                 this.dom.style.display = '';
21992                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21993             };
21994             var triggerIndex = 'Trigger'+(index+1);
21995
21996             if(this['hide'+triggerIndex]){
21997                 t.dom.style.display = 'none';
21998             }
21999             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22000             t.addClassOnOver('x-form-trigger-over');
22001             t.addClassOnClick('x-form-trigger-click');
22002         }, this);
22003         this.triggers = ts.elements;
22004     },
22005
22006     onTrigger1Click : Roo.emptyFn,
22007     onTrigger2Click : Roo.emptyFn
22008 });/*
22009  * Based on:
22010  * Ext JS Library 1.1.1
22011  * Copyright(c) 2006-2007, Ext JS, LLC.
22012  *
22013  * Originally Released Under LGPL - original licence link has changed is not relivant.
22014  *
22015  * Fork - LGPL
22016  * <script type="text/javascript">
22017  */
22018  
22019 /**
22020  * @class Roo.form.TextArea
22021  * @extends Roo.form.TextField
22022  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22023  * support for auto-sizing.
22024  * @constructor
22025  * Creates a new TextArea
22026  * @param {Object} config Configuration options
22027  */
22028 Roo.form.TextArea = function(config){
22029     Roo.form.TextArea.superclass.constructor.call(this, config);
22030     // these are provided exchanges for backwards compat
22031     // minHeight/maxHeight were replaced by growMin/growMax to be
22032     // compatible with TextField growing config values
22033     if(this.minHeight !== undefined){
22034         this.growMin = this.minHeight;
22035     }
22036     if(this.maxHeight !== undefined){
22037         this.growMax = this.maxHeight;
22038     }
22039 };
22040
22041 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22042     /**
22043      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22044      */
22045     growMin : 60,
22046     /**
22047      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22048      */
22049     growMax: 1000,
22050     /**
22051      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22052      * in the field (equivalent to setting overflow: hidden, defaults to false)
22053      */
22054     preventScrollbars: false,
22055     /**
22056      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22057      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22058      */
22059
22060     // private
22061     onRender : function(ct, position){
22062         if(!this.el){
22063             this.defaultAutoCreate = {
22064                 tag: "textarea",
22065                 style:"width:300px;height:60px;",
22066                 autocomplete: "off"
22067             };
22068         }
22069         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22070         if(this.grow){
22071             this.textSizeEl = Roo.DomHelper.append(document.body, {
22072                 tag: "pre", cls: "x-form-grow-sizer"
22073             });
22074             if(this.preventScrollbars){
22075                 this.el.setStyle("overflow", "hidden");
22076             }
22077             this.el.setHeight(this.growMin);
22078         }
22079     },
22080
22081     onDestroy : function(){
22082         if(this.textSizeEl){
22083             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22084         }
22085         Roo.form.TextArea.superclass.onDestroy.call(this);
22086     },
22087
22088     // private
22089     onKeyUp : function(e){
22090         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22091             this.autoSize();
22092         }
22093     },
22094
22095     /**
22096      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22097      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22098      */
22099     autoSize : function(){
22100         if(!this.grow || !this.textSizeEl){
22101             return;
22102         }
22103         var el = this.el;
22104         var v = el.dom.value;
22105         var ts = this.textSizeEl;
22106
22107         ts.innerHTML = '';
22108         ts.appendChild(document.createTextNode(v));
22109         v = ts.innerHTML;
22110
22111         Roo.fly(ts).setWidth(this.el.getWidth());
22112         if(v.length < 1){
22113             v = "&#160;&#160;";
22114         }else{
22115             if(Roo.isIE){
22116                 v = v.replace(/\n/g, '<p>&#160;</p>');
22117             }
22118             v += "&#160;\n&#160;";
22119         }
22120         ts.innerHTML = v;
22121         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22122         if(h != this.lastHeight){
22123             this.lastHeight = h;
22124             this.el.setHeight(h);
22125             this.fireEvent("autosize", this, h);
22126         }
22127     }
22128 });/*
22129  * Based on:
22130  * Ext JS Library 1.1.1
22131  * Copyright(c) 2006-2007, Ext JS, LLC.
22132  *
22133  * Originally Released Under LGPL - original licence link has changed is not relivant.
22134  *
22135  * Fork - LGPL
22136  * <script type="text/javascript">
22137  */
22138  
22139
22140 /**
22141  * @class Roo.form.NumberField
22142  * @extends Roo.form.TextField
22143  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22144  * @constructor
22145  * Creates a new NumberField
22146  * @param {Object} config Configuration options
22147  */
22148 Roo.form.NumberField = function(config){
22149     Roo.form.NumberField.superclass.constructor.call(this, config);
22150 };
22151
22152 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22153     /**
22154      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22155      */
22156     fieldClass: "x-form-field x-form-num-field",
22157     /**
22158      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22159      */
22160     allowDecimals : true,
22161     /**
22162      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22163      */
22164     decimalSeparator : ".",
22165     /**
22166      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22167      */
22168     decimalPrecision : 2,
22169     /**
22170      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22171      */
22172     allowNegative : true,
22173     /**
22174      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22175      */
22176     minValue : Number.NEGATIVE_INFINITY,
22177     /**
22178      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22179      */
22180     maxValue : Number.MAX_VALUE,
22181     /**
22182      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22183      */
22184     minText : "The minimum value for this field is {0}",
22185     /**
22186      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22187      */
22188     maxText : "The maximum value for this field is {0}",
22189     /**
22190      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22191      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22192      */
22193     nanText : "{0} is not a valid number",
22194
22195     // private
22196     initEvents : function(){
22197         Roo.form.NumberField.superclass.initEvents.call(this);
22198         var allowed = "0123456789";
22199         if(this.allowDecimals){
22200             allowed += this.decimalSeparator;
22201         }
22202         if(this.allowNegative){
22203             allowed += "-";
22204         }
22205         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22206         var keyPress = function(e){
22207             var k = e.getKey();
22208             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22209                 return;
22210             }
22211             var c = e.getCharCode();
22212             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22213                 e.stopEvent();
22214             }
22215         };
22216         this.el.on("keypress", keyPress, this);
22217     },
22218
22219     // private
22220     validateValue : function(value){
22221         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22222             return false;
22223         }
22224         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22225              return true;
22226         }
22227         var num = this.parseValue(value);
22228         if(isNaN(num)){
22229             this.markInvalid(String.format(this.nanText, value));
22230             return false;
22231         }
22232         if(num < this.minValue){
22233             this.markInvalid(String.format(this.minText, this.minValue));
22234             return false;
22235         }
22236         if(num > this.maxValue){
22237             this.markInvalid(String.format(this.maxText, this.maxValue));
22238             return false;
22239         }
22240         return true;
22241     },
22242
22243     getValue : function(){
22244         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22245     },
22246
22247     // private
22248     parseValue : function(value){
22249         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22250         return isNaN(value) ? '' : value;
22251     },
22252
22253     // private
22254     fixPrecision : function(value){
22255         var nan = isNaN(value);
22256         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22257             return nan ? '' : value;
22258         }
22259         return parseFloat(value).toFixed(this.decimalPrecision);
22260     },
22261
22262     setValue : function(v){
22263         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22264     },
22265
22266     // private
22267     decimalPrecisionFcn : function(v){
22268         return Math.floor(v);
22269     },
22270
22271     beforeBlur : function(){
22272         var v = this.parseValue(this.getRawValue());
22273         if(v){
22274             this.setValue(this.fixPrecision(v));
22275         }
22276     }
22277 });/*
22278  * Based on:
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  *
22282  * Originally Released Under LGPL - original licence link has changed is not relivant.
22283  *
22284  * Fork - LGPL
22285  * <script type="text/javascript">
22286  */
22287  
22288 /**
22289  * @class Roo.form.DateField
22290  * @extends Roo.form.TriggerField
22291  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22292 * @constructor
22293 * Create a new DateField
22294 * @param {Object} config
22295  */
22296 Roo.form.DateField = function(config){
22297     Roo.form.DateField.superclass.constructor.call(this, config);
22298     
22299       this.addEvents({
22300          
22301         /**
22302          * @event select
22303          * Fires when a date is selected
22304              * @param {Roo.form.DateField} combo This combo box
22305              * @param {Date} date The date selected
22306              */
22307         'select' : true
22308          
22309     });
22310     
22311     
22312     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22313     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22314     this.ddMatch = null;
22315     if(this.disabledDates){
22316         var dd = this.disabledDates;
22317         var re = "(?:";
22318         for(var i = 0; i < dd.length; i++){
22319             re += dd[i];
22320             if(i != dd.length-1) re += "|";
22321         }
22322         this.ddMatch = new RegExp(re + ")");
22323     }
22324 };
22325
22326 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22327     /**
22328      * @cfg {String} format
22329      * The default date format string which can be overriden for localization support.  The format must be
22330      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22331      */
22332     format : "m/d/y",
22333     /**
22334      * @cfg {String} altFormats
22335      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22336      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22337      */
22338     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22339     /**
22340      * @cfg {Array} disabledDays
22341      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22342      */
22343     disabledDays : null,
22344     /**
22345      * @cfg {String} disabledDaysText
22346      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22347      */
22348     disabledDaysText : "Disabled",
22349     /**
22350      * @cfg {Array} disabledDates
22351      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22352      * expression so they are very powerful. Some examples:
22353      * <ul>
22354      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22355      * <li>["03/08", "09/16"] would disable those days for every year</li>
22356      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22357      * <li>["03/../2006"] would disable every day in March 2006</li>
22358      * <li>["^03"] would disable every day in every March</li>
22359      * </ul>
22360      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22361      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22362      */
22363     disabledDates : null,
22364     /**
22365      * @cfg {String} disabledDatesText
22366      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22367      */
22368     disabledDatesText : "Disabled",
22369     /**
22370      * @cfg {Date/String} minValue
22371      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22372      * valid format (defaults to null).
22373      */
22374     minValue : null,
22375     /**
22376      * @cfg {Date/String} maxValue
22377      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22378      * valid format (defaults to null).
22379      */
22380     maxValue : null,
22381     /**
22382      * @cfg {String} minText
22383      * The error text to display when the date in the cell is before minValue (defaults to
22384      * 'The date in this field must be after {minValue}').
22385      */
22386     minText : "The date in this field must be equal to or after {0}",
22387     /**
22388      * @cfg {String} maxText
22389      * The error text to display when the date in the cell is after maxValue (defaults to
22390      * 'The date in this field must be before {maxValue}').
22391      */
22392     maxText : "The date in this field must be equal to or before {0}",
22393     /**
22394      * @cfg {String} invalidText
22395      * The error text to display when the date in the field is invalid (defaults to
22396      * '{value} is not a valid date - it must be in the format {format}').
22397      */
22398     invalidText : "{0} is not a valid date - it must be in the format {1}",
22399     /**
22400      * @cfg {String} triggerClass
22401      * An additional CSS class used to style the trigger button.  The trigger will always get the
22402      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22403      * which displays a calendar icon).
22404      */
22405     triggerClass : 'x-form-date-trigger',
22406     
22407
22408     /**
22409      * @cfg {bool} useIso
22410      * if enabled, then the date field will use a hidden field to store the 
22411      * real value as iso formated date. default (false)
22412      */ 
22413     useIso : false,
22414     /**
22415      * @cfg {String/Object} autoCreate
22416      * A DomHelper element spec, or true for a default element spec (defaults to
22417      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22418      */ 
22419     // private
22420     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22421     
22422     // private
22423     hiddenField: false,
22424     
22425     onRender : function(ct, position)
22426     {
22427         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22428         if (this.useIso) {
22429             this.el.dom.removeAttribute('name'); 
22430             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22431                     'before', true);
22432             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22433             // prevent input submission
22434             this.hiddenName = this.name;
22435         }
22436             
22437             
22438     },
22439     
22440     // private
22441     validateValue : function(value)
22442     {
22443         value = this.formatDate(value);
22444         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22445             return false;
22446         }
22447         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22448              return true;
22449         }
22450         var svalue = value;
22451         value = this.parseDate(value);
22452         if(!value){
22453             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22454             return false;
22455         }
22456         var time = value.getTime();
22457         if(this.minValue && time < this.minValue.getTime()){
22458             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22459             return false;
22460         }
22461         if(this.maxValue && time > this.maxValue.getTime()){
22462             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22463             return false;
22464         }
22465         if(this.disabledDays){
22466             var day = value.getDay();
22467             for(var i = 0; i < this.disabledDays.length; i++) {
22468                 if(day === this.disabledDays[i]){
22469                     this.markInvalid(this.disabledDaysText);
22470                     return false;
22471                 }
22472             }
22473         }
22474         var fvalue = this.formatDate(value);
22475         if(this.ddMatch && this.ddMatch.test(fvalue)){
22476             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22477             return false;
22478         }
22479         return true;
22480     },
22481
22482     // private
22483     // Provides logic to override the default TriggerField.validateBlur which just returns true
22484     validateBlur : function(){
22485         return !this.menu || !this.menu.isVisible();
22486     },
22487
22488     /**
22489      * Returns the current date value of the date field.
22490      * @return {Date} The date value
22491      */
22492     getValue : function(){
22493         
22494         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22495     },
22496
22497     /**
22498      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22499      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22500      * (the default format used is "m/d/y").
22501      * <br />Usage:
22502      * <pre><code>
22503 //All of these calls set the same date value (May 4, 2006)
22504
22505 //Pass a date object:
22506 var dt = new Date('5/4/06');
22507 dateField.setValue(dt);
22508
22509 //Pass a date string (default format):
22510 dateField.setValue('5/4/06');
22511
22512 //Pass a date string (custom format):
22513 dateField.format = 'Y-m-d';
22514 dateField.setValue('2006-5-4');
22515 </code></pre>
22516      * @param {String/Date} date The date or valid date string
22517      */
22518     setValue : function(date){
22519         if (this.hiddenField) {
22520             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22521         }
22522         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22523     },
22524
22525     // private
22526     parseDate : function(value){
22527         if(!value || value instanceof Date){
22528             return value;
22529         }
22530         var v = Date.parseDate(value, this.format);
22531         if(!v && this.altFormats){
22532             if(!this.altFormatsArray){
22533                 this.altFormatsArray = this.altFormats.split("|");
22534             }
22535             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22536                 v = Date.parseDate(value, this.altFormatsArray[i]);
22537             }
22538         }
22539         return v;
22540     },
22541
22542     // private
22543     formatDate : function(date, fmt){
22544         return (!date || !(date instanceof Date)) ?
22545                date : date.dateFormat(fmt || this.format);
22546     },
22547
22548     // private
22549     menuListeners : {
22550         select: function(m, d){
22551             this.setValue(d);
22552             this.fireEvent('select', this, d);
22553         },
22554         show : function(){ // retain focus styling
22555             this.onFocus();
22556         },
22557         hide : function(){
22558             this.focus.defer(10, this);
22559             var ml = this.menuListeners;
22560             this.menu.un("select", ml.select,  this);
22561             this.menu.un("show", ml.show,  this);
22562             this.menu.un("hide", ml.hide,  this);
22563         }
22564     },
22565
22566     // private
22567     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22568     onTriggerClick : function(){
22569         if(this.disabled){
22570             return;
22571         }
22572         if(this.menu == null){
22573             this.menu = new Roo.menu.DateMenu();
22574         }
22575         Roo.apply(this.menu.picker,  {
22576             showClear: this.allowBlank,
22577             minDate : this.minValue,
22578             maxDate : this.maxValue,
22579             disabledDatesRE : this.ddMatch,
22580             disabledDatesText : this.disabledDatesText,
22581             disabledDays : this.disabledDays,
22582             disabledDaysText : this.disabledDaysText,
22583             format : this.format,
22584             minText : String.format(this.minText, this.formatDate(this.minValue)),
22585             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22586         });
22587         this.menu.on(Roo.apply({}, this.menuListeners, {
22588             scope:this
22589         }));
22590         this.menu.picker.setValue(this.getValue() || new Date());
22591         this.menu.show(this.el, "tl-bl?");
22592     },
22593
22594     beforeBlur : function(){
22595         var v = this.parseDate(this.getRawValue());
22596         if(v){
22597             this.setValue(v);
22598         }
22599     }
22600
22601     /** @cfg {Boolean} grow @hide */
22602     /** @cfg {Number} growMin @hide */
22603     /** @cfg {Number} growMax @hide */
22604     /**
22605      * @hide
22606      * @method autoSize
22607      */
22608 });/*
22609  * Based on:
22610  * Ext JS Library 1.1.1
22611  * Copyright(c) 2006-2007, Ext JS, LLC.
22612  *
22613  * Originally Released Under LGPL - original licence link has changed is not relivant.
22614  *
22615  * Fork - LGPL
22616  * <script type="text/javascript">
22617  */
22618  
22619
22620 /**
22621  * @class Roo.form.ComboBox
22622  * @extends Roo.form.TriggerField
22623  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22624  * @constructor
22625  * Create a new ComboBox.
22626  * @param {Object} config Configuration options
22627  */
22628 Roo.form.ComboBox = function(config){
22629     Roo.form.ComboBox.superclass.constructor.call(this, config);
22630     this.addEvents({
22631         /**
22632          * @event expand
22633          * Fires when the dropdown list is expanded
22634              * @param {Roo.form.ComboBox} combo This combo box
22635              */
22636         'expand' : true,
22637         /**
22638          * @event collapse
22639          * Fires when the dropdown list is collapsed
22640              * @param {Roo.form.ComboBox} combo This combo box
22641              */
22642         'collapse' : true,
22643         /**
22644          * @event beforeselect
22645          * Fires before a list item is selected. Return false to cancel the selection.
22646              * @param {Roo.form.ComboBox} combo This combo box
22647              * @param {Roo.data.Record} record The data record returned from the underlying store
22648              * @param {Number} index The index of the selected item in the dropdown list
22649              */
22650         'beforeselect' : true,
22651         /**
22652          * @event select
22653          * Fires when a list item is selected
22654              * @param {Roo.form.ComboBox} combo This combo box
22655              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22656              * @param {Number} index The index of the selected item in the dropdown list
22657              */
22658         'select' : true,
22659         /**
22660          * @event beforequery
22661          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22662          * The event object passed has these properties:
22663              * @param {Roo.form.ComboBox} combo This combo box
22664              * @param {String} query The query
22665              * @param {Boolean} forceAll true to force "all" query
22666              * @param {Boolean} cancel true to cancel the query
22667              * @param {Object} e The query event object
22668              */
22669         'beforequery': true,
22670          /**
22671          * @event add
22672          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22673              * @param {Roo.form.ComboBox} combo This combo box
22674              */
22675         'add' : true,
22676         /**
22677          * @event edit
22678          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22679              * @param {Roo.form.ComboBox} combo This combo box
22680              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22681              */
22682         'edit' : true
22683         
22684         
22685     });
22686     if(this.transform){
22687         this.allowDomMove = false;
22688         var s = Roo.getDom(this.transform);
22689         if(!this.hiddenName){
22690             this.hiddenName = s.name;
22691         }
22692         if(!this.store){
22693             this.mode = 'local';
22694             var d = [], opts = s.options;
22695             for(var i = 0, len = opts.length;i < len; i++){
22696                 var o = opts[i];
22697                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22698                 if(o.selected) {
22699                     this.value = value;
22700                 }
22701                 d.push([value, o.text]);
22702             }
22703             this.store = new Roo.data.SimpleStore({
22704                 'id': 0,
22705                 fields: ['value', 'text'],
22706                 data : d
22707             });
22708             this.valueField = 'value';
22709             this.displayField = 'text';
22710         }
22711         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22712         if(!this.lazyRender){
22713             this.target = true;
22714             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22715             s.parentNode.removeChild(s); // remove it
22716             this.render(this.el.parentNode);
22717         }else{
22718             s.parentNode.removeChild(s); // remove it
22719         }
22720
22721     }
22722     if (this.store) {
22723         this.store = Roo.factory(this.store, Roo.data);
22724     }
22725     
22726     this.selectedIndex = -1;
22727     if(this.mode == 'local'){
22728         if(config.queryDelay === undefined){
22729             this.queryDelay = 10;
22730         }
22731         if(config.minChars === undefined){
22732             this.minChars = 0;
22733         }
22734     }
22735 };
22736
22737 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22738     /**
22739      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22740      */
22741     /**
22742      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22743      * rendering into an Roo.Editor, defaults to false)
22744      */
22745     /**
22746      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22747      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22748      */
22749     /**
22750      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22751      */
22752     /**
22753      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22754      * the dropdown list (defaults to undefined, with no header element)
22755      */
22756
22757      /**
22758      * @cfg {String/Roo.Template} tpl The template to use to render the output
22759      */
22760      
22761     // private
22762     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22763     /**
22764      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22765      */
22766     listWidth: undefined,
22767     /**
22768      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22769      * mode = 'remote' or 'text' if mode = 'local')
22770      */
22771     displayField: undefined,
22772     /**
22773      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22774      * mode = 'remote' or 'value' if mode = 'local'). 
22775      * Note: use of a valueField requires the user make a selection
22776      * in order for a value to be mapped.
22777      */
22778     valueField: undefined,
22779     /**
22780      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22781      * field's data value (defaults to the underlying DOM element's name)
22782      */
22783     hiddenName: undefined,
22784     /**
22785      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22786      */
22787     listClass: '',
22788     /**
22789      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22790      */
22791     selectedClass: 'x-combo-selected',
22792     /**
22793      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22794      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22795      * which displays a downward arrow icon).
22796      */
22797     triggerClass : 'x-form-arrow-trigger',
22798     /**
22799      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22800      */
22801     shadow:'sides',
22802     /**
22803      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22804      * anchor positions (defaults to 'tl-bl')
22805      */
22806     listAlign: 'tl-bl?',
22807     /**
22808      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22809      */
22810     maxHeight: 300,
22811     /**
22812      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22813      * query specified by the allQuery config option (defaults to 'query')
22814      */
22815     triggerAction: 'query',
22816     /**
22817      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22818      * (defaults to 4, does not apply if editable = false)
22819      */
22820     minChars : 4,
22821     /**
22822      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22823      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22824      */
22825     typeAhead: false,
22826     /**
22827      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22828      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22829      */
22830     queryDelay: 500,
22831     /**
22832      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22833      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22834      */
22835     pageSize: 0,
22836     /**
22837      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22838      * when editable = true (defaults to false)
22839      */
22840     selectOnFocus:false,
22841     /**
22842      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22843      */
22844     queryParam: 'query',
22845     /**
22846      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22847      * when mode = 'remote' (defaults to 'Loading...')
22848      */
22849     loadingText: 'Loading...',
22850     /**
22851      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22852      */
22853     resizable: false,
22854     /**
22855      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22856      */
22857     handleHeight : 8,
22858     /**
22859      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22860      * traditional select (defaults to true)
22861      */
22862     editable: true,
22863     /**
22864      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22865      */
22866     allQuery: '',
22867     /**
22868      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22869      */
22870     mode: 'remote',
22871     /**
22872      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22873      * listWidth has a higher value)
22874      */
22875     minListWidth : 70,
22876     /**
22877      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22878      * allow the user to set arbitrary text into the field (defaults to false)
22879      */
22880     forceSelection:false,
22881     /**
22882      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22883      * if typeAhead = true (defaults to 250)
22884      */
22885     typeAheadDelay : 250,
22886     /**
22887      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22888      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22889      */
22890     valueNotFoundText : undefined,
22891     /**
22892      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22893      */
22894     blockFocus : false,
22895     
22896     /**
22897      * @cfg {Boolean} disableClear Disable showing of clear button.
22898      */
22899     disableClear : false,
22900     /**
22901      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22902      */
22903     alwaysQuery : false,
22904     
22905     //private
22906     addicon : false,
22907     editicon: false,
22908     
22909     
22910     // private
22911     onRender : function(ct, position){
22912         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22913         if(this.hiddenName){
22914             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22915                     'before', true);
22916             this.hiddenField.value =
22917                 this.hiddenValue !== undefined ? this.hiddenValue :
22918                 this.value !== undefined ? this.value : '';
22919
22920             // prevent input submission
22921             this.el.dom.removeAttribute('name');
22922         }
22923         if(Roo.isGecko){
22924             this.el.dom.setAttribute('autocomplete', 'off');
22925         }
22926
22927         var cls = 'x-combo-list';
22928
22929         this.list = new Roo.Layer({
22930             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22931         });
22932
22933         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22934         this.list.setWidth(lw);
22935         this.list.swallowEvent('mousewheel');
22936         this.assetHeight = 0;
22937
22938         if(this.title){
22939             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22940             this.assetHeight += this.header.getHeight();
22941         }
22942
22943         this.innerList = this.list.createChild({cls:cls+'-inner'});
22944         this.innerList.on('mouseover', this.onViewOver, this);
22945         this.innerList.on('mousemove', this.onViewMove, this);
22946         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22947         
22948         if(this.allowBlank && !this.pageSize && !this.disableClear){
22949             this.footer = this.list.createChild({cls:cls+'-ft'});
22950             this.pageTb = new Roo.Toolbar(this.footer);
22951            
22952         }
22953         if(this.pageSize){
22954             this.footer = this.list.createChild({cls:cls+'-ft'});
22955             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22956                     {pageSize: this.pageSize});
22957             
22958         }
22959         
22960         if (this.pageTb && this.allowBlank && !this.disableClear) {
22961             var _this = this;
22962             this.pageTb.add(new Roo.Toolbar.Fill(), {
22963                 cls: 'x-btn-icon x-btn-clear',
22964                 text: '&#160;',
22965                 handler: function()
22966                 {
22967                     _this.collapse();
22968                     _this.clearValue();
22969                     _this.onSelect(false, -1);
22970                 }
22971             });
22972         }
22973         if (this.footer) {
22974             this.assetHeight += this.footer.getHeight();
22975         }
22976         
22977
22978         if(!this.tpl){
22979             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22980         }
22981
22982         this.view = new Roo.View(this.innerList, this.tpl, {
22983             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22984         });
22985
22986         this.view.on('click', this.onViewClick, this);
22987
22988         this.store.on('beforeload', this.onBeforeLoad, this);
22989         this.store.on('load', this.onLoad, this);
22990         this.store.on('loadexception', this.collapse, this);
22991
22992         if(this.resizable){
22993             this.resizer = new Roo.Resizable(this.list,  {
22994                pinned:true, handles:'se'
22995             });
22996             this.resizer.on('resize', function(r, w, h){
22997                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22998                 this.listWidth = w;
22999                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23000                 this.restrictHeight();
23001             }, this);
23002             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23003         }
23004         if(!this.editable){
23005             this.editable = true;
23006             this.setEditable(false);
23007         }  
23008         
23009         
23010         if (typeof(this.events.add.listeners) != 'undefined') {
23011             
23012             this.addicon = this.wrap.createChild(
23013                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23014        
23015             this.addicon.on('click', function(e) {
23016                 this.fireEvent('add', this);
23017             }, this);
23018         }
23019         if (typeof(this.events.edit.listeners) != 'undefined') {
23020             
23021             this.editicon = this.wrap.createChild(
23022                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23023             if (this.addicon) {
23024                 this.editicon.setStyle('margin-left', '40px');
23025             }
23026             this.editicon.on('click', function(e) {
23027                 
23028                 // we fire even  if inothing is selected..
23029                 this.fireEvent('edit', this, this.lastData );
23030                 
23031             }, this);
23032         }
23033         
23034         
23035         
23036     },
23037
23038     // private
23039     initEvents : function(){
23040         Roo.form.ComboBox.superclass.initEvents.call(this);
23041
23042         this.keyNav = new Roo.KeyNav(this.el, {
23043             "up" : function(e){
23044                 this.inKeyMode = true;
23045                 this.selectPrev();
23046             },
23047
23048             "down" : function(e){
23049                 if(!this.isExpanded()){
23050                     this.onTriggerClick();
23051                 }else{
23052                     this.inKeyMode = true;
23053                     this.selectNext();
23054                 }
23055             },
23056
23057             "enter" : function(e){
23058                 this.onViewClick();
23059                 //return true;
23060             },
23061
23062             "esc" : function(e){
23063                 this.collapse();
23064             },
23065
23066             "tab" : function(e){
23067                 this.onViewClick(false);
23068                 return true;
23069             },
23070
23071             scope : this,
23072
23073             doRelay : function(foo, bar, hname){
23074                 if(hname == 'down' || this.scope.isExpanded()){
23075                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23076                 }
23077                 return true;
23078             },
23079
23080             forceKeyDown: true
23081         });
23082         this.queryDelay = Math.max(this.queryDelay || 10,
23083                 this.mode == 'local' ? 10 : 250);
23084         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23085         if(this.typeAhead){
23086             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23087         }
23088         if(this.editable !== false){
23089             this.el.on("keyup", this.onKeyUp, this);
23090         }
23091         if(this.forceSelection){
23092             this.on('blur', this.doForce, this);
23093         }
23094     },
23095
23096     onDestroy : function(){
23097         if(this.view){
23098             this.view.setStore(null);
23099             this.view.el.removeAllListeners();
23100             this.view.el.remove();
23101             this.view.purgeListeners();
23102         }
23103         if(this.list){
23104             this.list.destroy();
23105         }
23106         if(this.store){
23107             this.store.un('beforeload', this.onBeforeLoad, this);
23108             this.store.un('load', this.onLoad, this);
23109             this.store.un('loadexception', this.collapse, this);
23110         }
23111         Roo.form.ComboBox.superclass.onDestroy.call(this);
23112     },
23113
23114     // private
23115     fireKey : function(e){
23116         if(e.isNavKeyPress() && !this.list.isVisible()){
23117             this.fireEvent("specialkey", this, e);
23118         }
23119     },
23120
23121     // private
23122     onResize: function(w, h){
23123         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23124         
23125         if(typeof w != 'number'){
23126             // we do not handle it!?!?
23127             return;
23128         }
23129         var tw = this.trigger.getWidth();
23130         tw += this.addicon ? this.addicon.getWidth() : 0;
23131         tw += this.editicon ? this.editicon.getWidth() : 0;
23132         var x = w - tw;
23133         this.el.setWidth( this.adjustWidth('input', x));
23134             
23135         this.trigger.setStyle('left', x+'px');
23136         
23137         if(this.list && this.listWidth === undefined){
23138             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23139             this.list.setWidth(lw);
23140             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23141         }
23142         
23143     
23144         
23145     },
23146
23147     /**
23148      * Allow or prevent the user from directly editing the field text.  If false is passed,
23149      * the user will only be able to select from the items defined in the dropdown list.  This method
23150      * is the runtime equivalent of setting the 'editable' config option at config time.
23151      * @param {Boolean} value True to allow the user to directly edit the field text
23152      */
23153     setEditable : function(value){
23154         if(value == this.editable){
23155             return;
23156         }
23157         this.editable = value;
23158         if(!value){
23159             this.el.dom.setAttribute('readOnly', true);
23160             this.el.on('mousedown', this.onTriggerClick,  this);
23161             this.el.addClass('x-combo-noedit');
23162         }else{
23163             this.el.dom.setAttribute('readOnly', false);
23164             this.el.un('mousedown', this.onTriggerClick,  this);
23165             this.el.removeClass('x-combo-noedit');
23166         }
23167     },
23168
23169     // private
23170     onBeforeLoad : function(){
23171         if(!this.hasFocus){
23172             return;
23173         }
23174         this.innerList.update(this.loadingText ?
23175                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23176         this.restrictHeight();
23177         this.selectedIndex = -1;
23178     },
23179
23180     // private
23181     onLoad : function(){
23182         if(!this.hasFocus){
23183             return;
23184         }
23185         if(this.store.getCount() > 0){
23186             this.expand();
23187             this.restrictHeight();
23188             if(this.lastQuery == this.allQuery){
23189                 if(this.editable){
23190                     this.el.dom.select();
23191                 }
23192                 if(!this.selectByValue(this.value, true)){
23193                     this.select(0, true);
23194                 }
23195             }else{
23196                 this.selectNext();
23197                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23198                     this.taTask.delay(this.typeAheadDelay);
23199                 }
23200             }
23201         }else{
23202             this.onEmptyResults();
23203         }
23204         //this.el.focus();
23205     },
23206
23207     // private
23208     onTypeAhead : function(){
23209         if(this.store.getCount() > 0){
23210             var r = this.store.getAt(0);
23211             var newValue = r.data[this.displayField];
23212             var len = newValue.length;
23213             var selStart = this.getRawValue().length;
23214             if(selStart != len){
23215                 this.setRawValue(newValue);
23216                 this.selectText(selStart, newValue.length);
23217             }
23218         }
23219     },
23220
23221     // private
23222     onSelect : function(record, index){
23223         if(this.fireEvent('beforeselect', this, record, index) !== false){
23224             this.setFromData(index > -1 ? record.data : false);
23225             this.collapse();
23226             this.fireEvent('select', this, record, index);
23227         }
23228     },
23229
23230     /**
23231      * Returns the currently selected field value or empty string if no value is set.
23232      * @return {String} value The selected value
23233      */
23234     getValue : function(){
23235         if(this.valueField){
23236             return typeof this.value != 'undefined' ? this.value : '';
23237         }else{
23238             return Roo.form.ComboBox.superclass.getValue.call(this);
23239         }
23240     },
23241
23242     /**
23243      * Clears any text/value currently set in the field
23244      */
23245     clearValue : function(){
23246         if(this.hiddenField){
23247             this.hiddenField.value = '';
23248         }
23249         this.value = '';
23250         this.setRawValue('');
23251         this.lastSelectionText = '';
23252         this.applyEmptyText();
23253     },
23254
23255     /**
23256      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23257      * will be displayed in the field.  If the value does not match the data value of an existing item,
23258      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23259      * Otherwise the field will be blank (although the value will still be set).
23260      * @param {String} value The value to match
23261      */
23262     setValue : function(v){
23263         var text = v;
23264         if(this.valueField){
23265             var r = this.findRecord(this.valueField, v);
23266             if(r){
23267                 text = r.data[this.displayField];
23268             }else if(this.valueNotFoundText !== undefined){
23269                 text = this.valueNotFoundText;
23270             }
23271         }
23272         this.lastSelectionText = text;
23273         if(this.hiddenField){
23274             this.hiddenField.value = v;
23275         }
23276         Roo.form.ComboBox.superclass.setValue.call(this, text);
23277         this.value = v;
23278     },
23279     /**
23280      * @property {Object} the last set data for the element
23281      */
23282     
23283     lastData : false,
23284     /**
23285      * Sets the value of the field based on a object which is related to the record format for the store.
23286      * @param {Object} value the value to set as. or false on reset?
23287      */
23288     setFromData : function(o){
23289         var dv = ''; // display value
23290         var vv = ''; // value value..
23291         this.lastData = o;
23292         if (this.displayField) {
23293             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23294         } else {
23295             // this is an error condition!!!
23296             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23297         }
23298         
23299         if(this.valueField){
23300             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23301         }
23302         if(this.hiddenField){
23303             this.hiddenField.value = vv;
23304             
23305             this.lastSelectionText = dv;
23306             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23307             this.value = vv;
23308             return;
23309         }
23310         // no hidden field.. - we store the value in 'value', but still display
23311         // display field!!!!
23312         this.lastSelectionText = dv;
23313         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23314         this.value = vv;
23315         
23316         
23317     },
23318     // private
23319     reset : function(){
23320         // overridden so that last data is reset..
23321         this.setValue(this.originalValue);
23322         this.clearInvalid();
23323         this.lastData = false;
23324     },
23325     // private
23326     findRecord : function(prop, value){
23327         var record;
23328         if(this.store.getCount() > 0){
23329             this.store.each(function(r){
23330                 if(r.data[prop] == value){
23331                     record = r;
23332                     return false;
23333                 }
23334             });
23335         }
23336         return record;
23337     },
23338
23339     // private
23340     onViewMove : function(e, t){
23341         this.inKeyMode = false;
23342     },
23343
23344     // private
23345     onViewOver : function(e, t){
23346         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23347             return;
23348         }
23349         var item = this.view.findItemFromChild(t);
23350         if(item){
23351             var index = this.view.indexOf(item);
23352             this.select(index, false);
23353         }
23354     },
23355
23356     // private
23357     onViewClick : function(doFocus){
23358         var index = this.view.getSelectedIndexes()[0];
23359         var r = this.store.getAt(index);
23360         if(r){
23361             this.onSelect(r, index);
23362         }
23363         if(doFocus !== false && !this.blockFocus){
23364             this.el.focus();
23365         }
23366     },
23367
23368     // private
23369     restrictHeight : function(){
23370         this.innerList.dom.style.height = '';
23371         var inner = this.innerList.dom;
23372         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23373         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23374         this.list.beginUpdate();
23375         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23376         this.list.alignTo(this.el, this.listAlign);
23377         this.list.endUpdate();
23378     },
23379
23380     // private
23381     onEmptyResults : function(){
23382         this.collapse();
23383     },
23384
23385     /**
23386      * Returns true if the dropdown list is expanded, else false.
23387      */
23388     isExpanded : function(){
23389         return this.list.isVisible();
23390     },
23391
23392     /**
23393      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23395      * @param {String} value The data value of the item to select
23396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23397      * selected item if it is not currently in view (defaults to true)
23398      * @return {Boolean} True if the value matched an item in the list, else false
23399      */
23400     selectByValue : function(v, scrollIntoView){
23401         if(v !== undefined && v !== null){
23402             var r = this.findRecord(this.valueField || this.displayField, v);
23403             if(r){
23404                 this.select(this.store.indexOf(r), scrollIntoView);
23405                 return true;
23406             }
23407         }
23408         return false;
23409     },
23410
23411     /**
23412      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23414      * @param {Number} index The zero-based index of the list item to select
23415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23416      * selected item if it is not currently in view (defaults to true)
23417      */
23418     select : function(index, scrollIntoView){
23419         this.selectedIndex = index;
23420         this.view.select(index);
23421         if(scrollIntoView !== false){
23422             var el = this.view.getNode(index);
23423             if(el){
23424                 this.innerList.scrollChildIntoView(el, false);
23425             }
23426         }
23427     },
23428
23429     // private
23430     selectNext : function(){
23431         var ct = this.store.getCount();
23432         if(ct > 0){
23433             if(this.selectedIndex == -1){
23434                 this.select(0);
23435             }else if(this.selectedIndex < ct-1){
23436                 this.select(this.selectedIndex+1);
23437             }
23438         }
23439     },
23440
23441     // private
23442     selectPrev : function(){
23443         var ct = this.store.getCount();
23444         if(ct > 0){
23445             if(this.selectedIndex == -1){
23446                 this.select(0);
23447             }else if(this.selectedIndex != 0){
23448                 this.select(this.selectedIndex-1);
23449             }
23450         }
23451     },
23452
23453     // private
23454     onKeyUp : function(e){
23455         if(this.editable !== false && !e.isSpecialKey()){
23456             this.lastKey = e.getKey();
23457             this.dqTask.delay(this.queryDelay);
23458         }
23459     },
23460
23461     // private
23462     validateBlur : function(){
23463         return !this.list || !this.list.isVisible();   
23464     },
23465
23466     // private
23467     initQuery : function(){
23468         this.doQuery(this.getRawValue());
23469     },
23470
23471     // private
23472     doForce : function(){
23473         if(this.el.dom.value.length > 0){
23474             this.el.dom.value =
23475                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23476             this.applyEmptyText();
23477         }
23478     },
23479
23480     /**
23481      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23482      * query allowing the query action to be canceled if needed.
23483      * @param {String} query The SQL query to execute
23484      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23485      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23486      * saved in the current store (defaults to false)
23487      */
23488     doQuery : function(q, forceAll){
23489         if(q === undefined || q === null){
23490             q = '';
23491         }
23492         var qe = {
23493             query: q,
23494             forceAll: forceAll,
23495             combo: this,
23496             cancel:false
23497         };
23498         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23499             return false;
23500         }
23501         q = qe.query;
23502         forceAll = qe.forceAll;
23503         if(forceAll === true || (q.length >= this.minChars)){
23504             if(this.lastQuery != q || this.alwaysQuery){
23505                 this.lastQuery = q;
23506                 if(this.mode == 'local'){
23507                     this.selectedIndex = -1;
23508                     if(forceAll){
23509                         this.store.clearFilter();
23510                     }else{
23511                         this.store.filter(this.displayField, q);
23512                     }
23513                     this.onLoad();
23514                 }else{
23515                     this.store.baseParams[this.queryParam] = q;
23516                     this.store.load({
23517                         params: this.getParams(q)
23518                     });
23519                     this.expand();
23520                 }
23521             }else{
23522                 this.selectedIndex = -1;
23523                 this.onLoad();   
23524             }
23525         }
23526     },
23527
23528     // private
23529     getParams : function(q){
23530         var p = {};
23531         //p[this.queryParam] = q;
23532         if(this.pageSize){
23533             p.start = 0;
23534             p.limit = this.pageSize;
23535         }
23536         return p;
23537     },
23538
23539     /**
23540      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23541      */
23542     collapse : function(){
23543         if(!this.isExpanded()){
23544             return;
23545         }
23546         this.list.hide();
23547         Roo.get(document).un('mousedown', this.collapseIf, this);
23548         Roo.get(document).un('mousewheel', this.collapseIf, this);
23549         if (!this.editable) {
23550             Roo.get(document).un('keydown', this.listKeyPress, this);
23551         }
23552         this.fireEvent('collapse', this);
23553     },
23554
23555     // private
23556     collapseIf : function(e){
23557         if(!e.within(this.wrap) && !e.within(this.list)){
23558             this.collapse();
23559         }
23560     },
23561
23562     /**
23563      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23564      */
23565     expand : function(){
23566         if(this.isExpanded() || !this.hasFocus){
23567             return;
23568         }
23569         this.list.alignTo(this.el, this.listAlign);
23570         this.list.show();
23571         Roo.get(document).on('mousedown', this.collapseIf, this);
23572         Roo.get(document).on('mousewheel', this.collapseIf, this);
23573         if (!this.editable) {
23574             Roo.get(document).on('keydown', this.listKeyPress, this);
23575         }
23576         
23577         this.fireEvent('expand', this);
23578     },
23579
23580     // private
23581     // Implements the default empty TriggerField.onTriggerClick function
23582     onTriggerClick : function(){
23583         if(this.disabled){
23584             return;
23585         }
23586         if(this.isExpanded()){
23587             this.collapse();
23588             if (!this.blockFocus) {
23589                 this.el.focus();
23590             }
23591             
23592         }else {
23593             this.hasFocus = true;
23594             if(this.triggerAction == 'all') {
23595                 this.doQuery(this.allQuery, true);
23596             } else {
23597                 this.doQuery(this.getRawValue());
23598             }
23599             if (!this.blockFocus) {
23600                 this.el.focus();
23601             }
23602         }
23603     },
23604     listKeyPress : function(e)
23605     {
23606         //Roo.log('listkeypress');
23607         // scroll to first matching element based on key pres..
23608         if (e.isSpecialKey()) {
23609             return false;
23610         }
23611         var k = String.fromCharCode(e.getKey()).toUpperCase();
23612         //Roo.log(k);
23613         var match  = false;
23614         var csel = this.view.getSelectedNodes();
23615         var cselitem = false;
23616         if (csel.length) {
23617             var ix = this.view.indexOf(csel[0]);
23618             cselitem  = this.store.getAt(ix);
23619             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23620                 cselitem = false;
23621             }
23622             
23623         }
23624         
23625         this.store.each(function(v) { 
23626             if (cselitem) {
23627                 // start at existing selection.
23628                 if (cselitem.id == v.id) {
23629                     cselitem = false;
23630                 }
23631                 return;
23632             }
23633                 
23634             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23635                 match = this.store.indexOf(v);
23636                 return false;
23637             }
23638         }, this);
23639         
23640         if (match === false) {
23641             return true; // no more action?
23642         }
23643         // scroll to?
23644         this.view.select(match);
23645         var sn = Roo.get(this.view.getSelectedNodes()[0])
23646         sn.scrollIntoView(sn.dom.parentNode, false);
23647     }
23648
23649     /** 
23650     * @cfg {Boolean} grow 
23651     * @hide 
23652     */
23653     /** 
23654     * @cfg {Number} growMin 
23655     * @hide 
23656     */
23657     /** 
23658     * @cfg {Number} growMax 
23659     * @hide 
23660     */
23661     /**
23662      * @hide
23663      * @method autoSize
23664      */
23665 });/*
23666  * Based on:
23667  * Ext JS Library 1.1.1
23668  * Copyright(c) 2006-2007, Ext JS, LLC.
23669  *
23670  * Originally Released Under LGPL - original licence link has changed is not relivant.
23671  *
23672  * Fork - LGPL
23673  * <script type="text/javascript">
23674  */
23675 /**
23676  * @class Roo.form.Checkbox
23677  * @extends Roo.form.Field
23678  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23679  * @constructor
23680  * Creates a new Checkbox
23681  * @param {Object} config Configuration options
23682  */
23683 Roo.form.Checkbox = function(config){
23684     Roo.form.Checkbox.superclass.constructor.call(this, config);
23685     this.addEvents({
23686         /**
23687          * @event check
23688          * Fires when the checkbox is checked or unchecked.
23689              * @param {Roo.form.Checkbox} this This checkbox
23690              * @param {Boolean} checked The new checked value
23691              */
23692         check : true
23693     });
23694 };
23695
23696 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23697     /**
23698      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23699      */
23700     focusClass : undefined,
23701     /**
23702      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23703      */
23704     fieldClass: "x-form-field",
23705     /**
23706      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23707      */
23708     checked: false,
23709     /**
23710      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23711      * {tag: "input", type: "checkbox", autocomplete: "off"})
23712      */
23713     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23714     /**
23715      * @cfg {String} boxLabel The text that appears beside the checkbox
23716      */
23717     boxLabel : "",
23718     /**
23719      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23720      */  
23721     inputValue : '1',
23722     /**
23723      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23724      */
23725      valueOff: '0', // value when not checked..
23726
23727     actionMode : 'viewEl', 
23728     //
23729     // private
23730     itemCls : 'x-menu-check-item x-form-item',
23731     groupClass : 'x-menu-group-item',
23732     inputType : 'hidden',
23733     
23734     
23735     inSetChecked: false, // check that we are not calling self...
23736     
23737     inputElement: false, // real input element?
23738     basedOn: false, // ????
23739     
23740     isFormField: true, // not sure where this is needed!!!!
23741
23742     onResize : function(){
23743         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23744         if(!this.boxLabel){
23745             this.el.alignTo(this.wrap, 'c-c');
23746         }
23747     },
23748
23749     initEvents : function(){
23750         Roo.form.Checkbox.superclass.initEvents.call(this);
23751         this.el.on("click", this.onClick,  this);
23752         this.el.on("change", this.onClick,  this);
23753     },
23754
23755
23756     getResizeEl : function(){
23757         return this.wrap;
23758     },
23759
23760     getPositionEl : function(){
23761         return this.wrap;
23762     },
23763
23764     // private
23765     onRender : function(ct, position){
23766         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23767         /*
23768         if(this.inputValue !== undefined){
23769             this.el.dom.value = this.inputValue;
23770         }
23771         */
23772         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23773         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23774         var viewEl = this.wrap.createChild({ 
23775             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23776         this.viewEl = viewEl;   
23777         this.wrap.on('click', this.onClick,  this); 
23778         
23779         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23780         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23781         
23782         
23783         
23784         if(this.boxLabel){
23785             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23786         //    viewEl.on('click', this.onClick,  this); 
23787         }
23788         //if(this.checked){
23789             this.setChecked(this.checked);
23790         //}else{
23791             //this.checked = this.el.dom;
23792         //}
23793
23794     },
23795
23796     // private
23797     initValue : Roo.emptyFn,
23798
23799     /**
23800      * Returns the checked state of the checkbox.
23801      * @return {Boolean} True if checked, else false
23802      */
23803     getValue : function(){
23804         if(this.el){
23805             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23806         }
23807         return this.valueOff;
23808         
23809     },
23810
23811         // private
23812     onClick : function(){ 
23813         this.setChecked(!this.checked);
23814
23815         //if(this.el.dom.checked != this.checked){
23816         //    this.setValue(this.el.dom.checked);
23817        // }
23818     },
23819
23820     /**
23821      * Sets the checked state of the checkbox.
23822      * On is always based on a string comparison between inputValue and the param.
23823      * @param {Boolean/String} value - the value to set 
23824      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23825      */
23826     setValue : function(v,suppressEvent){
23827         
23828         
23829         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23830         //if(this.el && this.el.dom){
23831         //    this.el.dom.checked = this.checked;
23832         //    this.el.dom.defaultChecked = this.checked;
23833         //}
23834         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23835         //this.fireEvent("check", this, this.checked);
23836     },
23837     // private..
23838     setChecked : function(state,suppressEvent)
23839     {
23840         if (this.inSetChecked) {
23841             this.checked = state;
23842             return;
23843         }
23844         
23845     
23846         if(this.wrap){
23847             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23848         }
23849         this.checked = state;
23850         if(suppressEvent !== true){
23851             this.fireEvent('check', this, state);
23852         }
23853         this.inSetChecked = true;
23854         this.el.dom.value = state ? this.inputValue : this.valueOff;
23855         this.inSetChecked = false;
23856         
23857     },
23858     // handle setting of hidden value by some other method!!?!?
23859     setFromHidden: function()
23860     {
23861         if(!this.el){
23862             return;
23863         }
23864         //console.log("SET FROM HIDDEN");
23865         //alert('setFrom hidden');
23866         this.setValue(this.el.dom.value);
23867     },
23868     
23869     onDestroy : function()
23870     {
23871         if(this.viewEl){
23872             Roo.get(this.viewEl).remove();
23873         }
23874          
23875         Roo.form.Checkbox.superclass.onDestroy.call(this);
23876     }
23877
23878 });/*
23879  * Based on:
23880  * Ext JS Library 1.1.1
23881  * Copyright(c) 2006-2007, Ext JS, LLC.
23882  *
23883  * Originally Released Under LGPL - original licence link has changed is not relivant.
23884  *
23885  * Fork - LGPL
23886  * <script type="text/javascript">
23887  */
23888  
23889 /**
23890  * @class Roo.form.Radio
23891  * @extends Roo.form.Checkbox
23892  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23893  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23894  * @constructor
23895  * Creates a new Radio
23896  * @param {Object} config Configuration options
23897  */
23898 Roo.form.Radio = function(){
23899     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23900 };
23901 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23902     inputType: 'radio',
23903
23904     /**
23905      * If this radio is part of a group, it will return the selected value
23906      * @return {String}
23907      */
23908     getGroupValue : function(){
23909         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23910     }
23911 });//<script type="text/javascript">
23912
23913 /*
23914  * Ext JS Library 1.1.1
23915  * Copyright(c) 2006-2007, Ext JS, LLC.
23916  * licensing@extjs.com
23917  * 
23918  * http://www.extjs.com/license
23919  */
23920  
23921  /*
23922   * 
23923   * Known bugs:
23924   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23925   * - IE ? - no idea how much works there.
23926   * 
23927   * 
23928   * 
23929   */
23930  
23931
23932 /**
23933  * @class Ext.form.HtmlEditor
23934  * @extends Ext.form.Field
23935  * Provides a lightweight HTML Editor component.
23936  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23937  * 
23938  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23939  * supported by this editor.</b><br/><br/>
23940  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23941  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23942  */
23943 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23944       /**
23945      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23946      */
23947     toolbars : false,
23948     /**
23949      * @cfg {String} createLinkText The default text for the create link prompt
23950      */
23951     createLinkText : 'Please enter the URL for the link:',
23952     /**
23953      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23954      */
23955     defaultLinkValue : 'http:/'+'/',
23956    
23957      /**
23958      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23959      *                        Roo.resizable.
23960      */
23961     resizable : false,
23962      /**
23963      * @cfg {Number} height (in pixels)
23964      */   
23965     height: 300,
23966    /**
23967      * @cfg {Number} width (in pixels)
23968      */   
23969     width: 500,
23970     // id of frame..
23971     frameId: false,
23972     
23973     // private properties
23974     validationEvent : false,
23975     deferHeight: true,
23976     initialized : false,
23977     activated : false,
23978     sourceEditMode : false,
23979     onFocus : Roo.emptyFn,
23980     iframePad:3,
23981     hideMode:'offsets',
23982     
23983     defaultAutoCreate : { // modified by initCompnoent..
23984         tag: "textarea",
23985         style:"width:500px;height:300px;",
23986         autocomplete: "off"
23987     },
23988
23989     // private
23990     initComponent : function(){
23991         this.addEvents({
23992             /**
23993              * @event initialize
23994              * Fires when the editor is fully initialized (including the iframe)
23995              * @param {HtmlEditor} this
23996              */
23997             initialize: true,
23998             /**
23999              * @event activate
24000              * Fires when the editor is first receives the focus. Any insertion must wait
24001              * until after this event.
24002              * @param {HtmlEditor} this
24003              */
24004             activate: true,
24005              /**
24006              * @event beforesync
24007              * Fires before the textarea is updated with content from the editor iframe. Return false
24008              * to cancel the sync.
24009              * @param {HtmlEditor} this
24010              * @param {String} html
24011              */
24012             beforesync: true,
24013              /**
24014              * @event beforepush
24015              * Fires before the iframe editor is updated with content from the textarea. Return false
24016              * to cancel the push.
24017              * @param {HtmlEditor} this
24018              * @param {String} html
24019              */
24020             beforepush: true,
24021              /**
24022              * @event sync
24023              * Fires when the textarea is updated with content from the editor iframe.
24024              * @param {HtmlEditor} this
24025              * @param {String} html
24026              */
24027             sync: true,
24028              /**
24029              * @event push
24030              * Fires when the iframe editor is updated with content from the textarea.
24031              * @param {HtmlEditor} this
24032              * @param {String} html
24033              */
24034             push: true,
24035              /**
24036              * @event editmodechange
24037              * Fires when the editor switches edit modes
24038              * @param {HtmlEditor} this
24039              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24040              */
24041             editmodechange: true,
24042             /**
24043              * @event editorevent
24044              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24045              * @param {HtmlEditor} this
24046              */
24047             editorevent: true
24048         });
24049         this.defaultAutoCreate =  {
24050             tag: "textarea",
24051             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24052             autocomplete: "off"
24053         };
24054     },
24055
24056     /**
24057      * Protected method that will not generally be called directly. It
24058      * is called when the editor creates its toolbar. Override this method if you need to
24059      * add custom toolbar buttons.
24060      * @param {HtmlEditor} editor
24061      */
24062     createToolbar : function(editor){
24063         if (!editor.toolbars || !editor.toolbars.length) {
24064             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24065         }
24066         
24067         for (var i =0 ; i < editor.toolbars.length;i++) {
24068             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24069             editor.toolbars[i].init(editor);
24070         }
24071          
24072         
24073     },
24074
24075     /**
24076      * Protected method that will not generally be called directly. It
24077      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24078      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24079      */
24080     getDocMarkup : function(){
24081         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24082     },
24083
24084     // private
24085     onRender : function(ct, position)
24086     {
24087         var _t = this;
24088         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24089         this.el.dom.style.border = '0 none';
24090         this.el.dom.setAttribute('tabIndex', -1);
24091         this.el.addClass('x-hidden');
24092         if(Roo.isIE){ // fix IE 1px bogus margin
24093             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24094         }
24095         this.wrap = this.el.wrap({
24096             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24097         });
24098         
24099         if (this.resizable) {
24100             this.resizeEl = new Roo.Resizable(this.wrap, {
24101                 pinned : true,
24102                 wrap: true,
24103                 dynamic : true,
24104                 minHeight : this.height,
24105                 height: this.height,
24106                 handles : this.resizable,
24107                 width: this.width,
24108                 listeners : {
24109                     resize : function(r, w, h) {
24110                         _t.onResize(w,h); // -something
24111                     }
24112                 }
24113             });
24114             
24115         }
24116
24117         this.frameId = Roo.id();
24118         this.createToolbar(this);
24119         
24120         
24121         
24122         
24123       
24124         
24125         var iframe = this.wrap.createChild({
24126             tag: 'iframe',
24127             id: this.frameId,
24128             name: this.frameId,
24129             frameBorder : 'no',
24130             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24131         });
24132         
24133        // console.log(iframe);
24134         //this.wrap.dom.appendChild(iframe);
24135
24136         this.iframe = iframe.dom;
24137
24138          this.assignDocWin();
24139         
24140         this.doc.designMode = 'on';
24141        
24142         this.doc.open();
24143         this.doc.write(this.getDocMarkup());
24144         this.doc.close();
24145
24146         
24147         var task = { // must defer to wait for browser to be ready
24148             run : function(){
24149                 //console.log("run task?" + this.doc.readyState);
24150                 this.assignDocWin();
24151                 if(this.doc.body || this.doc.readyState == 'complete'){
24152                     try {
24153                         this.doc.designMode="on";
24154                     } catch (e) {
24155                         return;
24156                     }
24157                     Roo.TaskMgr.stop(task);
24158                     this.initEditor.defer(10, this);
24159                 }
24160             },
24161             interval : 10,
24162             duration:10000,
24163             scope: this
24164         };
24165         Roo.TaskMgr.start(task);
24166
24167         if(!this.width){
24168             this.setSize(this.wrap.getSize());
24169         }
24170         if (this.resizeEl) {
24171             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24172             // should trigger onReize..
24173         }
24174     },
24175
24176     // private
24177     onResize : function(w, h)
24178     {
24179         //Roo.log('resize: ' +w + ',' + h );
24180         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24181         if(this.el && this.iframe){
24182             if(typeof w == 'number'){
24183                 var aw = w - this.wrap.getFrameWidth('lr');
24184                 this.el.setWidth(this.adjustWidth('textarea', aw));
24185                 this.iframe.style.width = aw + 'px';
24186             }
24187             if(typeof h == 'number'){
24188                 var tbh = 0;
24189                 for (var i =0; i < this.toolbars.length;i++) {
24190                     // fixme - ask toolbars for heights?
24191                     tbh += this.toolbars[i].tb.el.getHeight();
24192                 }
24193                 
24194                 
24195                 
24196                 
24197                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24198                 ah -= 10; // knock a few pixes off for look..
24199                 this.el.setHeight(this.adjustWidth('textarea', ah));
24200                 this.iframe.style.height = ah + 'px';
24201                 if(this.doc){
24202                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24203                 }
24204             }
24205         }
24206     },
24207
24208     /**
24209      * Toggles the editor between standard and source edit mode.
24210      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24211      */
24212     toggleSourceEdit : function(sourceEditMode){
24213         
24214         this.sourceEditMode = sourceEditMode === true;
24215         
24216         if(this.sourceEditMode){
24217           
24218             this.syncValue();
24219             this.iframe.className = 'x-hidden';
24220             this.el.removeClass('x-hidden');
24221             this.el.dom.removeAttribute('tabIndex');
24222             this.el.focus();
24223         }else{
24224              
24225             this.pushValue();
24226             this.iframe.className = '';
24227             this.el.addClass('x-hidden');
24228             this.el.dom.setAttribute('tabIndex', -1);
24229             this.deferFocus();
24230         }
24231         this.setSize(this.wrap.getSize());
24232         this.fireEvent('editmodechange', this, this.sourceEditMode);
24233     },
24234
24235     // private used internally
24236     createLink : function(){
24237         var url = prompt(this.createLinkText, this.defaultLinkValue);
24238         if(url && url != 'http:/'+'/'){
24239             this.relayCmd('createlink', url);
24240         }
24241     },
24242
24243     // private (for BoxComponent)
24244     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24245
24246     // private (for BoxComponent)
24247     getResizeEl : function(){
24248         return this.wrap;
24249     },
24250
24251     // private (for BoxComponent)
24252     getPositionEl : function(){
24253         return this.wrap;
24254     },
24255
24256     // private
24257     initEvents : function(){
24258         this.originalValue = this.getValue();
24259     },
24260
24261     /**
24262      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24263      * @method
24264      */
24265     markInvalid : Roo.emptyFn,
24266     /**
24267      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24268      * @method
24269      */
24270     clearInvalid : Roo.emptyFn,
24271
24272     setValue : function(v){
24273         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24274         this.pushValue();
24275     },
24276
24277     /**
24278      * Protected method that will not generally be called directly. If you need/want
24279      * custom HTML cleanup, this is the method you should override.
24280      * @param {String} html The HTML to be cleaned
24281      * return {String} The cleaned HTML
24282      */
24283     cleanHtml : function(html){
24284         html = String(html);
24285         if(html.length > 5){
24286             if(Roo.isSafari){ // strip safari nonsense
24287                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24288             }
24289         }
24290         if(html == '&nbsp;'){
24291             html = '';
24292         }
24293         return html;
24294     },
24295
24296     /**
24297      * Protected method that will not generally be called directly. Syncs the contents
24298      * of the editor iframe with the textarea.
24299      */
24300     syncValue : function(){
24301         if(this.initialized){
24302             var bd = (this.doc.body || this.doc.documentElement);
24303             this.cleanUpPaste();
24304             var html = bd.innerHTML;
24305             if(Roo.isSafari){
24306                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24307                 var m = bs.match(/text-align:(.*?);/i);
24308                 if(m && m[1]){
24309                     html = '<div style="'+m[0]+'">' + html + '</div>';
24310                 }
24311             }
24312             html = this.cleanHtml(html);
24313             if(this.fireEvent('beforesync', this, html) !== false){
24314                 this.el.dom.value = html;
24315                 this.fireEvent('sync', this, html);
24316             }
24317         }
24318     },
24319
24320     /**
24321      * Protected method that will not generally be called directly. Pushes the value of the textarea
24322      * into the iframe editor.
24323      */
24324     pushValue : function(){
24325         if(this.initialized){
24326             var v = this.el.dom.value;
24327             if(v.length < 1){
24328                 v = '&#160;';
24329             }
24330             
24331             if(this.fireEvent('beforepush', this, v) !== false){
24332                 var d = (this.doc.body || this.doc.documentElement);
24333                 d.innerHTML = v;
24334                 this.cleanUpPaste();
24335                 this.el.dom.value = d.innerHTML;
24336                 this.fireEvent('push', this, v);
24337             }
24338         }
24339     },
24340
24341     // private
24342     deferFocus : function(){
24343         this.focus.defer(10, this);
24344     },
24345
24346     // doc'ed in Field
24347     focus : function(){
24348         if(this.win && !this.sourceEditMode){
24349             this.win.focus();
24350         }else{
24351             this.el.focus();
24352         }
24353     },
24354     
24355     assignDocWin: function()
24356     {
24357         var iframe = this.iframe;
24358         
24359          if(Roo.isIE){
24360             this.doc = iframe.contentWindow.document;
24361             this.win = iframe.contentWindow;
24362         } else {
24363             if (!Roo.get(this.frameId)) {
24364                 return;
24365             }
24366             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24367             this.win = Roo.get(this.frameId).dom.contentWindow;
24368         }
24369     },
24370     
24371     // private
24372     initEditor : function(){
24373         //console.log("INIT EDITOR");
24374         this.assignDocWin();
24375         
24376         
24377         
24378         this.doc.designMode="on";
24379         this.doc.open();
24380         this.doc.write(this.getDocMarkup());
24381         this.doc.close();
24382         
24383         var dbody = (this.doc.body || this.doc.documentElement);
24384         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24385         // this copies styles from the containing element into thsi one..
24386         // not sure why we need all of this..
24387         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24388         ss['background-attachment'] = 'fixed'; // w3c
24389         dbody.bgProperties = 'fixed'; // ie
24390         Roo.DomHelper.applyStyles(dbody, ss);
24391         Roo.EventManager.on(this.doc, {
24392             'mousedown': this.onEditorEvent,
24393             'dblclick': this.onEditorEvent,
24394             'click': this.onEditorEvent,
24395             'keyup': this.onEditorEvent,
24396             buffer:100,
24397             scope: this
24398         });
24399         if(Roo.isGecko){
24400             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24401         }
24402         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24403             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24404         }
24405         this.initialized = true;
24406
24407         this.fireEvent('initialize', this);
24408         this.pushValue();
24409     },
24410
24411     // private
24412     onDestroy : function(){
24413         
24414         
24415         
24416         if(this.rendered){
24417             
24418             for (var i =0; i < this.toolbars.length;i++) {
24419                 // fixme - ask toolbars for heights?
24420                 this.toolbars[i].onDestroy();
24421             }
24422             
24423             this.wrap.dom.innerHTML = '';
24424             this.wrap.remove();
24425         }
24426     },
24427
24428     // private
24429     onFirstFocus : function(){
24430         
24431         this.assignDocWin();
24432         
24433         
24434         this.activated = true;
24435         for (var i =0; i < this.toolbars.length;i++) {
24436             this.toolbars[i].onFirstFocus();
24437         }
24438        
24439         if(Roo.isGecko){ // prevent silly gecko errors
24440             this.win.focus();
24441             var s = this.win.getSelection();
24442             if(!s.focusNode || s.focusNode.nodeType != 3){
24443                 var r = s.getRangeAt(0);
24444                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24445                 r.collapse(true);
24446                 this.deferFocus();
24447             }
24448             try{
24449                 this.execCmd('useCSS', true);
24450                 this.execCmd('styleWithCSS', false);
24451             }catch(e){}
24452         }
24453         this.fireEvent('activate', this);
24454     },
24455
24456     // private
24457     adjustFont: function(btn){
24458         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24459         //if(Roo.isSafari){ // safari
24460         //    adjust *= 2;
24461        // }
24462         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24463         if(Roo.isSafari){ // safari
24464             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24465             v =  (v < 10) ? 10 : v;
24466             v =  (v > 48) ? 48 : v;
24467             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24468             
24469         }
24470         
24471         
24472         v = Math.max(1, v+adjust);
24473         
24474         this.execCmd('FontSize', v  );
24475     },
24476
24477     onEditorEvent : function(e){
24478         this.fireEvent('editorevent', this, e);
24479       //  this.updateToolbar();
24480         this.syncValue();
24481     },
24482
24483     insertTag : function(tg)
24484     {
24485         // could be a bit smarter... -> wrap the current selected tRoo..
24486         
24487         this.execCmd("formatblock",   tg);
24488         
24489     },
24490     
24491     insertText : function(txt)
24492     {
24493         
24494         
24495         range = this.createRange();
24496         range.deleteContents();
24497                //alert(Sender.getAttribute('label'));
24498                
24499         range.insertNode(this.doc.createTextNode(txt));
24500     } ,
24501     
24502     // private
24503     relayBtnCmd : function(btn){
24504         this.relayCmd(btn.cmd);
24505     },
24506
24507     /**
24508      * Executes a Midas editor command on the editor document and performs necessary focus and
24509      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24510      * @param {String} cmd The Midas command
24511      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24512      */
24513     relayCmd : function(cmd, value){
24514         this.win.focus();
24515         this.execCmd(cmd, value);
24516         this.fireEvent('editorevent', this);
24517         //this.updateToolbar();
24518         this.deferFocus();
24519     },
24520
24521     /**
24522      * Executes a Midas editor command directly on the editor document.
24523      * For visual commands, you should use {@link #relayCmd} instead.
24524      * <b>This should only be called after the editor is initialized.</b>
24525      * @param {String} cmd The Midas command
24526      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24527      */
24528     execCmd : function(cmd, value){
24529         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24530         this.syncValue();
24531     },
24532
24533    
24534     /**
24535      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24536      * to insert tRoo.
24537      * @param {String} text
24538      */
24539     insertAtCursor : function(text){
24540         if(!this.activated){
24541             return;
24542         }
24543         if(Roo.isIE){
24544             this.win.focus();
24545             var r = this.doc.selection.createRange();
24546             if(r){
24547                 r.collapse(true);
24548                 r.pasteHTML(text);
24549                 this.syncValue();
24550                 this.deferFocus();
24551             }
24552         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24553             this.win.focus();
24554             this.execCmd('InsertHTML', text);
24555             this.deferFocus();
24556         }
24557     },
24558  // private
24559     mozKeyPress : function(e){
24560         if(e.ctrlKey){
24561             var c = e.getCharCode(), cmd;
24562           
24563             if(c > 0){
24564                 c = String.fromCharCode(c).toLowerCase();
24565                 switch(c){
24566                     case 'b':
24567                         cmd = 'bold';
24568                     break;
24569                     case 'i':
24570                         cmd = 'italic';
24571                     break;
24572                     case 'u':
24573                         cmd = 'underline';
24574                     case 'v':
24575                         this.cleanUpPaste.defer(100, this);
24576                         return;
24577                     break;
24578                 }
24579                 if(cmd){
24580                     this.win.focus();
24581                     this.execCmd(cmd);
24582                     this.deferFocus();
24583                     e.preventDefault();
24584                 }
24585                 
24586             }
24587         }
24588     },
24589
24590     // private
24591     fixKeys : function(){ // load time branching for fastest keydown performance
24592         if(Roo.isIE){
24593             return function(e){
24594                 var k = e.getKey(), r;
24595                 if(k == e.TAB){
24596                     e.stopEvent();
24597                     r = this.doc.selection.createRange();
24598                     if(r){
24599                         r.collapse(true);
24600                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24601                         this.deferFocus();
24602                     }
24603                     return;
24604                 }
24605                 
24606                 if(k == e.ENTER){
24607                     r = this.doc.selection.createRange();
24608                     if(r){
24609                         var target = r.parentElement();
24610                         if(!target || target.tagName.toLowerCase() != 'li'){
24611                             e.stopEvent();
24612                             r.pasteHTML('<br />');
24613                             r.collapse(false);
24614                             r.select();
24615                         }
24616                     }
24617                 }
24618                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24619                     this.cleanUpPaste.defer(100, this);
24620                     return;
24621                 }
24622                 
24623                 
24624             };
24625         }else if(Roo.isOpera){
24626             return function(e){
24627                 var k = e.getKey();
24628                 if(k == e.TAB){
24629                     e.stopEvent();
24630                     this.win.focus();
24631                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24632                     this.deferFocus();
24633                 }
24634                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24635                     this.cleanUpPaste.defer(100, this);
24636                     return;
24637                 }
24638                 
24639             };
24640         }else if(Roo.isSafari){
24641             return function(e){
24642                 var k = e.getKey();
24643                 
24644                 if(k == e.TAB){
24645                     e.stopEvent();
24646                     this.execCmd('InsertText','\t');
24647                     this.deferFocus();
24648                     return;
24649                 }
24650                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24651                     this.cleanUpPaste.defer(100, this);
24652                     return;
24653                 }
24654                 
24655              };
24656         }
24657     }(),
24658     
24659     getAllAncestors: function()
24660     {
24661         var p = this.getSelectedNode();
24662         var a = [];
24663         if (!p) {
24664             a.push(p); // push blank onto stack..
24665             p = this.getParentElement();
24666         }
24667         
24668         
24669         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24670             a.push(p);
24671             p = p.parentNode;
24672         }
24673         a.push(this.doc.body);
24674         return a;
24675     },
24676     lastSel : false,
24677     lastSelNode : false,
24678     
24679     
24680     getSelection : function() 
24681     {
24682         this.assignDocWin();
24683         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24684     },
24685     
24686     getSelectedNode: function() 
24687     {
24688         // this may only work on Gecko!!!
24689         
24690         // should we cache this!!!!
24691         
24692         
24693         
24694          
24695         var range = this.createRange(this.getSelection());
24696         
24697         if (Roo.isIE) {
24698             var parent = range.parentElement();
24699             while (true) {
24700                 var testRange = range.duplicate();
24701                 testRange.moveToElementText(parent);
24702                 if (testRange.inRange(range)) {
24703                     break;
24704                 }
24705                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24706                     break;
24707                 }
24708                 parent = parent.parentElement;
24709             }
24710             return parent;
24711         }
24712         
24713         
24714         var ar = range.endContainer.childNodes;
24715         if (!ar.length) {
24716             ar = range.commonAncestorContainer.childNodes;
24717             //alert(ar.length);
24718         }
24719         var nodes = [];
24720         var other_nodes = [];
24721         var has_other_nodes = false;
24722         for (var i=0;i<ar.length;i++) {
24723             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24724                 continue;
24725             }
24726             // fullly contained node.
24727             
24728             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24729                 nodes.push(ar[i]);
24730                 continue;
24731             }
24732             
24733             // probably selected..
24734             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24735                 other_nodes.push(ar[i]);
24736                 continue;
24737             }
24738             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24739                 continue;
24740             }
24741             
24742             
24743             has_other_nodes = true;
24744         }
24745         if (!nodes.length && other_nodes.length) {
24746             nodes= other_nodes;
24747         }
24748         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24749             return false;
24750         }
24751         
24752         return nodes[0];
24753     },
24754     createRange: function(sel)
24755     {
24756         // this has strange effects when using with 
24757         // top toolbar - not sure if it's a great idea.
24758         //this.editor.contentWindow.focus();
24759         if (typeof sel != "undefined") {
24760             try {
24761                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24762             } catch(e) {
24763                 return this.doc.createRange();
24764             }
24765         } else {
24766             return this.doc.createRange();
24767         }
24768     },
24769     getParentElement: function()
24770     {
24771         
24772         this.assignDocWin();
24773         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24774         
24775         var range = this.createRange(sel);
24776          
24777         try {
24778             var p = range.commonAncestorContainer;
24779             while (p.nodeType == 3) { // text node
24780                 p = p.parentNode;
24781             }
24782             return p;
24783         } catch (e) {
24784             return null;
24785         }
24786     
24787     },
24788     
24789     
24790     
24791     // BC Hacks - cause I cant work out what i was trying to do..
24792     rangeIntersectsNode : function(range, node)
24793     {
24794         var nodeRange = node.ownerDocument.createRange();
24795         try {
24796             nodeRange.selectNode(node);
24797         }
24798         catch (e) {
24799             nodeRange.selectNodeContents(node);
24800         }
24801
24802         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24803                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24804     },
24805     rangeCompareNode : function(range, node) {
24806         var nodeRange = node.ownerDocument.createRange();
24807         try {
24808             nodeRange.selectNode(node);
24809         } catch (e) {
24810             nodeRange.selectNodeContents(node);
24811         }
24812         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24813         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24814
24815         if (nodeIsBefore && !nodeIsAfter)
24816             return 0;
24817         if (!nodeIsBefore && nodeIsAfter)
24818             return 1;
24819         if (nodeIsBefore && nodeIsAfter)
24820             return 2;
24821
24822         return 3;
24823     },
24824
24825     // private? - in a new class?
24826     cleanUpPaste :  function()
24827     {
24828         // cleans up the whole document..
24829       //  console.log('cleanuppaste');
24830         this.cleanUpChildren(this.doc.body);
24831         
24832         
24833     },
24834     cleanUpChildren : function (n)
24835     {
24836         if (!n.childNodes.length) {
24837             return;
24838         }
24839         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24840            this.cleanUpChild(n.childNodes[i]);
24841         }
24842     },
24843     
24844     
24845         
24846     
24847     cleanUpChild : function (node)
24848     {
24849         //console.log(node);
24850         if (node.nodeName == "#text") {
24851             // clean up silly Windows -- stuff?
24852             return; 
24853         }
24854         if (node.nodeName == "#comment") {
24855             node.parentNode.removeChild(node);
24856             // clean up silly Windows -- stuff?
24857             return; 
24858         }
24859         
24860         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24861             // remove node.
24862             node.parentNode.removeChild(node);
24863             return;
24864             
24865         }
24866         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
24867             this.cleanUpChildren(node);
24868             // inserts everything just before this node...
24869             while (node.childNodes.length) {
24870                 var cn = node.childNodes[0];
24871                 node.removeChild(cn);
24872                 node.parentNode.insertBefore(cn, node);
24873             }
24874             node.parentNode.removeChild(node);
24875             return;
24876         }
24877         
24878         if (!node.attributes || !node.attributes.length) {
24879             this.cleanUpChildren(node);
24880             return;
24881         }
24882         
24883         function cleanAttr(n,v)
24884         {
24885             
24886             if (v.match(/^\./) || v.match(/^\//)) {
24887                 return;
24888             }
24889             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24890                 return;
24891             }
24892             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24893             node.removeAttribute(n);
24894             
24895         }
24896         
24897         function cleanStyle(n,v)
24898         {
24899             if (v.match(/expression/)) { //XSS?? should we even bother..
24900                 node.removeAttribute(n);
24901                 return;
24902             }
24903             
24904             
24905             var parts = v.split(/;/);
24906             Roo.each(parts, function(p) {
24907                 p = p.replace(/\s+/g,'');
24908                 if (!p.length) {
24909                     return true;
24910                 }
24911                 var l = p.split(':').shift().replace(/\s+/g,'');
24912                 
24913                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24914                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24915                     node.removeAttribute(n);
24916                     return false;
24917                 }
24918                 return true;
24919             });
24920             
24921             
24922         }
24923         
24924         
24925         for (var i = node.attributes.length-1; i > -1 ; i--) {
24926             var a = node.attributes[i];
24927             //console.log(a);
24928             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24929                 node.removeAttribute(a.name);
24930                 return;
24931             }
24932             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24933                 cleanAttr(a.name,a.value); // fixme..
24934                 return;
24935             }
24936             if (a.name == 'style') {
24937                 cleanStyle(a.name,a.value);
24938             }
24939             /// clean up MS crap..
24940             if (a.name == 'class') {
24941                 if (a.value.match(/^Mso/)) {
24942                     node.className = '';
24943                 }
24944             }
24945             
24946             // style cleanup!?
24947             // class cleanup?
24948             
24949         }
24950         
24951         
24952         this.cleanUpChildren(node);
24953         
24954         
24955     }
24956     
24957     
24958     // hide stuff that is not compatible
24959     /**
24960      * @event blur
24961      * @hide
24962      */
24963     /**
24964      * @event change
24965      * @hide
24966      */
24967     /**
24968      * @event focus
24969      * @hide
24970      */
24971     /**
24972      * @event specialkey
24973      * @hide
24974      */
24975     /**
24976      * @cfg {String} fieldClass @hide
24977      */
24978     /**
24979      * @cfg {String} focusClass @hide
24980      */
24981     /**
24982      * @cfg {String} autoCreate @hide
24983      */
24984     /**
24985      * @cfg {String} inputType @hide
24986      */
24987     /**
24988      * @cfg {String} invalidClass @hide
24989      */
24990     /**
24991      * @cfg {String} invalidText @hide
24992      */
24993     /**
24994      * @cfg {String} msgFx @hide
24995      */
24996     /**
24997      * @cfg {String} validateOnBlur @hide
24998      */
24999 });
25000
25001 Roo.form.HtmlEditor.white = [
25002         'area', 'br', 'img', 'input', 'hr', 'wbr',
25003         
25004        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25005        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25006        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25007        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25008        'table',   'ul',         'xmp', 
25009        
25010        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25011       'thead',   'tr', 
25012      
25013       'dir', 'menu', 'ol', 'ul', 'dl',
25014        
25015       'embed',  'object'
25016 ];
25017
25018
25019 Roo.form.HtmlEditor.black = [
25020     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25021         'applet', // 
25022         'base',   'basefont', 'bgsound', 'blink',  'body', 
25023         'frame',  'frameset', 'head',    'html',   'ilayer', 
25024         'iframe', 'layer',  'link',     'meta',    'object',   
25025         'script', 'style' ,'title',  'xml' // clean later..
25026 ];
25027 Roo.form.HtmlEditor.clean = [
25028     'script', 'style', 'title', 'xml'
25029 ];
25030 Roo.form.HtmlEditor.remove = [
25031     'font'
25032 ];
25033 // attributes..
25034
25035 Roo.form.HtmlEditor.ablack = [
25036     'on'
25037 ];
25038     
25039 Roo.form.HtmlEditor.aclean = [ 
25040     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25041 ];
25042
25043 // protocols..
25044 Roo.form.HtmlEditor.pwhite= [
25045         'http',  'https',  'mailto'
25046 ];
25047
25048 Roo.form.HtmlEditor.cwhite= [
25049         'text-align',
25050         'font-size'
25051 ];
25052
25053 // <script type="text/javascript">
25054 /*
25055  * Based on
25056  * Ext JS Library 1.1.1
25057  * Copyright(c) 2006-2007, Ext JS, LLC.
25058  *  
25059  
25060  */
25061
25062 /**
25063  * @class Roo.form.HtmlEditorToolbar1
25064  * Basic Toolbar
25065  * 
25066  * Usage:
25067  *
25068  new Roo.form.HtmlEditor({
25069     ....
25070     toolbars : [
25071         new Roo.form.HtmlEditorToolbar1({
25072             disable : { fonts: 1 , format: 1, ..., ... , ...],
25073             btns : [ .... ]
25074         })
25075     }
25076      
25077  * 
25078  * @cfg {Object} disable List of elements to disable..
25079  * @cfg {Array} btns List of additional buttons.
25080  * 
25081  * 
25082  * NEEDS Extra CSS? 
25083  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25084  */
25085  
25086 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25087 {
25088     
25089     Roo.apply(this, config);
25090     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25091     // dont call parent... till later.
25092 }
25093
25094 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25095     
25096     tb: false,
25097     
25098     rendered: false,
25099     
25100     editor : false,
25101     /**
25102      * @cfg {Object} disable  List of toolbar elements to disable
25103          
25104      */
25105     disable : false,
25106       /**
25107      * @cfg {Array} fontFamilies An array of available font families
25108      */
25109     fontFamilies : [
25110         'Arial',
25111         'Courier New',
25112         'Tahoma',
25113         'Times New Roman',
25114         'Verdana'
25115     ],
25116     
25117     specialChars : [
25118            "&#169;",
25119           "&#174;",     
25120           "&#8482;",    
25121           "&#163;" ,    
25122          // "&#8212;",    
25123           "&#8230;",    
25124           "&#247;" ,    
25125         //  "&#225;" ,     ?? a acute?
25126            "&#8364;"    , //Euro
25127        //   "&#8220;"    ,
25128         //  "&#8221;"    ,
25129         //  "&#8226;"    ,
25130           "&#176;"  //   , // degrees
25131
25132          // "&#233;"     , // e ecute
25133          // "&#250;"     , // u ecute?
25134     ],
25135     inputElements : [ 
25136             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25137             "input:submit", "input:button", "select", "textarea", "label" ],
25138     formats : [
25139         ["p"] ,  
25140         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25141         ["pre"],[ "code"], 
25142         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25143     ],
25144      /**
25145      * @cfg {String} defaultFont default font to use.
25146      */
25147     defaultFont: 'tahoma',
25148    
25149     fontSelect : false,
25150     
25151     
25152     formatCombo : false,
25153     
25154     init : function(editor)
25155     {
25156         this.editor = editor;
25157         
25158         
25159         var fid = editor.frameId;
25160         var etb = this;
25161         function btn(id, toggle, handler){
25162             var xid = fid + '-'+ id ;
25163             return {
25164                 id : xid,
25165                 cmd : id,
25166                 cls : 'x-btn-icon x-edit-'+id,
25167                 enableToggle:toggle !== false,
25168                 scope: editor, // was editor...
25169                 handler:handler||editor.relayBtnCmd,
25170                 clickEvent:'mousedown',
25171                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25172                 tabIndex:-1
25173             };
25174         }
25175         
25176         
25177         
25178         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25179         this.tb = tb;
25180          // stop form submits
25181         tb.el.on('click', function(e){
25182             e.preventDefault(); // what does this do?
25183         });
25184
25185         if(!this.disable.font && !Roo.isSafari){
25186             /* why no safari for fonts
25187             editor.fontSelect = tb.el.createChild({
25188                 tag:'select',
25189                 tabIndex: -1,
25190                 cls:'x-font-select',
25191                 html: editor.createFontOptions()
25192             });
25193             editor.fontSelect.on('change', function(){
25194                 var font = editor.fontSelect.dom.value;
25195                 editor.relayCmd('fontname', font);
25196                 editor.deferFocus();
25197             }, editor);
25198             tb.add(
25199                 editor.fontSelect.dom,
25200                 '-'
25201             );
25202             */
25203         };
25204         if(!this.disable.formats){
25205             this.formatCombo = new Roo.form.ComboBox({
25206                 store: new Roo.data.SimpleStore({
25207                     id : 'tag',
25208                     fields: ['tag'],
25209                     data : this.formats // from states.js
25210                 }),
25211                 blockFocus : true,
25212                 //autoCreate : {tag: "div",  size: "20"},
25213                 displayField:'tag',
25214                 typeAhead: false,
25215                 mode: 'local',
25216                 editable : false,
25217                 triggerAction: 'all',
25218                 emptyText:'Add tag',
25219                 selectOnFocus:true,
25220                 width:135,
25221                 listeners : {
25222                     'select': function(c, r, i) {
25223                         editor.insertTag(r.get('tag'));
25224                         editor.focus();
25225                     }
25226                 }
25227
25228             });
25229             tb.addField(this.formatCombo);
25230             
25231         }
25232         
25233         if(!this.disable.format){
25234             tb.add(
25235                 btn('bold'),
25236                 btn('italic'),
25237                 btn('underline')
25238             );
25239         };
25240         if(!this.disable.fontSize){
25241             tb.add(
25242                 '-',
25243                 
25244                 
25245                 btn('increasefontsize', false, editor.adjustFont),
25246                 btn('decreasefontsize', false, editor.adjustFont)
25247             );
25248         };
25249         
25250         
25251         if(this.disable.colors){
25252             tb.add(
25253                 '-', {
25254                     id:editor.frameId +'-forecolor',
25255                     cls:'x-btn-icon x-edit-forecolor',
25256                     clickEvent:'mousedown',
25257                     tooltip: this.buttonTips['forecolor'] || undefined,
25258                     tabIndex:-1,
25259                     menu : new Roo.menu.ColorMenu({
25260                         allowReselect: true,
25261                         focus: Roo.emptyFn,
25262                         value:'000000',
25263                         plain:true,
25264                         selectHandler: function(cp, color){
25265                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25266                             editor.deferFocus();
25267                         },
25268                         scope: editor,
25269                         clickEvent:'mousedown'
25270                     })
25271                 }, {
25272                     id:editor.frameId +'backcolor',
25273                     cls:'x-btn-icon x-edit-backcolor',
25274                     clickEvent:'mousedown',
25275                     tooltip: this.buttonTips['backcolor'] || undefined,
25276                     tabIndex:-1,
25277                     menu : new Roo.menu.ColorMenu({
25278                         focus: Roo.emptyFn,
25279                         value:'FFFFFF',
25280                         plain:true,
25281                         allowReselect: true,
25282                         selectHandler: function(cp, color){
25283                             if(Roo.isGecko){
25284                                 editor.execCmd('useCSS', false);
25285                                 editor.execCmd('hilitecolor', color);
25286                                 editor.execCmd('useCSS', true);
25287                                 editor.deferFocus();
25288                             }else{
25289                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25290                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25291                                 editor.deferFocus();
25292                             }
25293                         },
25294                         scope:editor,
25295                         clickEvent:'mousedown'
25296                     })
25297                 }
25298             );
25299         };
25300         // now add all the items...
25301         
25302
25303         if(!this.disable.alignments){
25304             tb.add(
25305                 '-',
25306                 btn('justifyleft'),
25307                 btn('justifycenter'),
25308                 btn('justifyright')
25309             );
25310         };
25311
25312         //if(!Roo.isSafari){
25313             if(!this.disable.links){
25314                 tb.add(
25315                     '-',
25316                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25317                 );
25318             };
25319
25320             if(!this.disable.lists){
25321                 tb.add(
25322                     '-',
25323                     btn('insertorderedlist'),
25324                     btn('insertunorderedlist')
25325                 );
25326             }
25327             if(!this.disable.sourceEdit){
25328                 tb.add(
25329                     '-',
25330                     btn('sourceedit', true, function(btn){
25331                         this.toggleSourceEdit(btn.pressed);
25332                     })
25333                 );
25334             }
25335         //}
25336         
25337         var smenu = { };
25338         // special menu.. - needs to be tidied up..
25339         if (!this.disable.special) {
25340             smenu = {
25341                 text: "&#169;",
25342                 cls: 'x-edit-none',
25343                 menu : {
25344                     items : []
25345                    }
25346             };
25347             for (var i =0; i < this.specialChars.length; i++) {
25348                 smenu.menu.items.push({
25349                     
25350                     html: this.specialChars[i],
25351                     handler: function(a,b) {
25352                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25353                         
25354                     },
25355                     tabIndex:-1
25356                 });
25357             }
25358             
25359             
25360             tb.add(smenu);
25361             
25362             
25363         }
25364         if (this.btns) {
25365             for(var i =0; i< this.btns.length;i++) {
25366                 var b = this.btns[i];
25367                 b.cls =  'x-edit-none';
25368                 b.scope = editor;
25369                 tb.add(b);
25370             }
25371         
25372         }
25373         
25374         
25375         
25376         // disable everything...
25377         
25378         this.tb.items.each(function(item){
25379            if(item.id != editor.frameId+ '-sourceedit'){
25380                 item.disable();
25381             }
25382         });
25383         this.rendered = true;
25384         
25385         // the all the btns;
25386         editor.on('editorevent', this.updateToolbar, this);
25387         // other toolbars need to implement this..
25388         //editor.on('editmodechange', this.updateToolbar, this);
25389     },
25390     
25391     
25392     
25393     /**
25394      * Protected method that will not generally be called directly. It triggers
25395      * a toolbar update by reading the markup state of the current selection in the editor.
25396      */
25397     updateToolbar: function(){
25398
25399         if(!this.editor.activated){
25400             this.editor.onFirstFocus();
25401             return;
25402         }
25403
25404         var btns = this.tb.items.map, 
25405             doc = this.editor.doc,
25406             frameId = this.editor.frameId;
25407
25408         if(!this.disable.font && !Roo.isSafari){
25409             /*
25410             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25411             if(name != this.fontSelect.dom.value){
25412                 this.fontSelect.dom.value = name;
25413             }
25414             */
25415         }
25416         if(!this.disable.format){
25417             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25418             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25419             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25420         }
25421         if(!this.disable.alignments){
25422             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25423             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25424             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25425         }
25426         if(!Roo.isSafari && !this.disable.lists){
25427             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25428             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25429         }
25430         
25431         var ans = this.editor.getAllAncestors();
25432         if (this.formatCombo) {
25433             
25434             
25435             var store = this.formatCombo.store;
25436             this.formatCombo.setValue("");
25437             for (var i =0; i < ans.length;i++) {
25438                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25439                     // select it..
25440                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25441                     break;
25442                 }
25443             }
25444         }
25445         
25446         
25447         
25448         // hides menus... - so this cant be on a menu...
25449         Roo.menu.MenuMgr.hideAll();
25450
25451         //this.editorsyncValue();
25452     },
25453    
25454     
25455     createFontOptions : function(){
25456         var buf = [], fs = this.fontFamilies, ff, lc;
25457         for(var i = 0, len = fs.length; i< len; i++){
25458             ff = fs[i];
25459             lc = ff.toLowerCase();
25460             buf.push(
25461                 '<option value="',lc,'" style="font-family:',ff,';"',
25462                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25463                     ff,
25464                 '</option>'
25465             );
25466         }
25467         return buf.join('');
25468     },
25469     
25470     toggleSourceEdit : function(sourceEditMode){
25471         if(sourceEditMode === undefined){
25472             sourceEditMode = !this.sourceEditMode;
25473         }
25474         this.sourceEditMode = sourceEditMode === true;
25475         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25476         // just toggle the button?
25477         if(btn.pressed !== this.editor.sourceEditMode){
25478             btn.toggle(this.editor.sourceEditMode);
25479             return;
25480         }
25481         
25482         if(this.sourceEditMode){
25483             this.tb.items.each(function(item){
25484                 if(item.cmd != 'sourceedit'){
25485                     item.disable();
25486                 }
25487             });
25488           
25489         }else{
25490             if(this.initialized){
25491                 this.tb.items.each(function(item){
25492                     item.enable();
25493                 });
25494             }
25495             
25496         }
25497         // tell the editor that it's been pressed..
25498         this.editor.toggleSourceEdit(sourceEditMode);
25499        
25500     },
25501      /**
25502      * Object collection of toolbar tooltips for the buttons in the editor. The key
25503      * is the command id associated with that button and the value is a valid QuickTips object.
25504      * For example:
25505 <pre><code>
25506 {
25507     bold : {
25508         title: 'Bold (Ctrl+B)',
25509         text: 'Make the selected text bold.',
25510         cls: 'x-html-editor-tip'
25511     },
25512     italic : {
25513         title: 'Italic (Ctrl+I)',
25514         text: 'Make the selected text italic.',
25515         cls: 'x-html-editor-tip'
25516     },
25517     ...
25518 </code></pre>
25519     * @type Object
25520      */
25521     buttonTips : {
25522         bold : {
25523             title: 'Bold (Ctrl+B)',
25524             text: 'Make the selected text bold.',
25525             cls: 'x-html-editor-tip'
25526         },
25527         italic : {
25528             title: 'Italic (Ctrl+I)',
25529             text: 'Make the selected text italic.',
25530             cls: 'x-html-editor-tip'
25531         },
25532         underline : {
25533             title: 'Underline (Ctrl+U)',
25534             text: 'Underline the selected text.',
25535             cls: 'x-html-editor-tip'
25536         },
25537         increasefontsize : {
25538             title: 'Grow Text',
25539             text: 'Increase the font size.',
25540             cls: 'x-html-editor-tip'
25541         },
25542         decreasefontsize : {
25543             title: 'Shrink Text',
25544             text: 'Decrease the font size.',
25545             cls: 'x-html-editor-tip'
25546         },
25547         backcolor : {
25548             title: 'Text Highlight Color',
25549             text: 'Change the background color of the selected text.',
25550             cls: 'x-html-editor-tip'
25551         },
25552         forecolor : {
25553             title: 'Font Color',
25554             text: 'Change the color of the selected text.',
25555             cls: 'x-html-editor-tip'
25556         },
25557         justifyleft : {
25558             title: 'Align Text Left',
25559             text: 'Align text to the left.',
25560             cls: 'x-html-editor-tip'
25561         },
25562         justifycenter : {
25563             title: 'Center Text',
25564             text: 'Center text in the editor.',
25565             cls: 'x-html-editor-tip'
25566         },
25567         justifyright : {
25568             title: 'Align Text Right',
25569             text: 'Align text to the right.',
25570             cls: 'x-html-editor-tip'
25571         },
25572         insertunorderedlist : {
25573             title: 'Bullet List',
25574             text: 'Start a bulleted list.',
25575             cls: 'x-html-editor-tip'
25576         },
25577         insertorderedlist : {
25578             title: 'Numbered List',
25579             text: 'Start a numbered list.',
25580             cls: 'x-html-editor-tip'
25581         },
25582         createlink : {
25583             title: 'Hyperlink',
25584             text: 'Make the selected text a hyperlink.',
25585             cls: 'x-html-editor-tip'
25586         },
25587         sourceedit : {
25588             title: 'Source Edit',
25589             text: 'Switch to source editing mode.',
25590             cls: 'x-html-editor-tip'
25591         }
25592     },
25593     // private
25594     onDestroy : function(){
25595         if(this.rendered){
25596             
25597             this.tb.items.each(function(item){
25598                 if(item.menu){
25599                     item.menu.removeAll();
25600                     if(item.menu.el){
25601                         item.menu.el.destroy();
25602                     }
25603                 }
25604                 item.destroy();
25605             });
25606              
25607         }
25608     },
25609     onFirstFocus: function() {
25610         this.tb.items.each(function(item){
25611            item.enable();
25612         });
25613     }
25614 });
25615
25616
25617
25618
25619 // <script type="text/javascript">
25620 /*
25621  * Based on
25622  * Ext JS Library 1.1.1
25623  * Copyright(c) 2006-2007, Ext JS, LLC.
25624  *  
25625  
25626  */
25627
25628  
25629 /**
25630  * @class Roo.form.HtmlEditor.ToolbarContext
25631  * Context Toolbar
25632  * 
25633  * Usage:
25634  *
25635  new Roo.form.HtmlEditor({
25636     ....
25637     toolbars : [
25638         new Roo.form.HtmlEditor.ToolbarStandard(),
25639         new Roo.form.HtmlEditor.ToolbarContext()
25640         })
25641     }
25642      
25643  * 
25644  * @config : {Object} disable List of elements to disable.. (not done yet.)
25645  * 
25646  * 
25647  */
25648
25649 Roo.form.HtmlEditor.ToolbarContext = function(config)
25650 {
25651     
25652     Roo.apply(this, config);
25653     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25654     // dont call parent... till later.
25655 }
25656 Roo.form.HtmlEditor.ToolbarContext.types = {
25657     'IMG' : {
25658         width : {
25659             title: "Width",
25660             width: 40
25661         },
25662         height:  {
25663             title: "Height",
25664             width: 40
25665         },
25666         align: {
25667             title: "Align",
25668             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25669             width : 80
25670             
25671         },
25672         border: {
25673             title: "Border",
25674             width: 40
25675         },
25676         alt: {
25677             title: "Alt",
25678             width: 120
25679         },
25680         src : {
25681             title: "Src",
25682             width: 220
25683         }
25684         
25685     },
25686     'A' : {
25687         name : {
25688             title: "Name",
25689             width: 50
25690         },
25691         href:  {
25692             title: "Href",
25693             width: 220
25694         } // border?
25695         
25696     },
25697     'TABLE' : {
25698         rows : {
25699             title: "Rows",
25700             width: 20
25701         },
25702         cols : {
25703             title: "Cols",
25704             width: 20
25705         },
25706         width : {
25707             title: "Width",
25708             width: 40
25709         },
25710         height : {
25711             title: "Height",
25712             width: 40
25713         },
25714         border : {
25715             title: "Border",
25716             width: 20
25717         }
25718     },
25719     'TD' : {
25720         width : {
25721             title: "Width",
25722             width: 40
25723         },
25724         height : {
25725             title: "Height",
25726             width: 40
25727         },   
25728         align: {
25729             title: "Align",
25730             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25731             width: 40
25732         },
25733         valign: {
25734             title: "Valign",
25735             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25736             width: 40
25737         },
25738         colspan: {
25739             title: "Colspan",
25740             width: 20
25741             
25742         }
25743     },
25744     'INPUT' : {
25745         name : {
25746             title: "name",
25747             width: 120
25748         },
25749         value : {
25750             title: "Value",
25751             width: 120
25752         },
25753         width : {
25754             title: "Width",
25755             width: 40
25756         }
25757     },
25758     'LABEL' : {
25759         'for' : {
25760             title: "For",
25761             width: 120
25762         }
25763     },
25764     'TEXTAREA' : {
25765           name : {
25766             title: "name",
25767             width: 120
25768         },
25769         rows : {
25770             title: "Rows",
25771             width: 20
25772         },
25773         cols : {
25774             title: "Cols",
25775             width: 20
25776         }
25777     },
25778     'SELECT' : {
25779         name : {
25780             title: "name",
25781             width: 120
25782         },
25783         selectoptions : {
25784             title: "Options",
25785             width: 200
25786         }
25787     },
25788     'BODY' : {
25789         title : {
25790             title: "title",
25791             width: 120,
25792             disabled : true
25793         }
25794     }
25795 };
25796
25797
25798
25799 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25800     
25801     tb: false,
25802     
25803     rendered: false,
25804     
25805     editor : false,
25806     /**
25807      * @cfg {Object} disable  List of toolbar elements to disable
25808          
25809      */
25810     disable : false,
25811     
25812     
25813     
25814     toolbars : false,
25815     
25816     init : function(editor)
25817     {
25818         this.editor = editor;
25819         
25820         
25821         var fid = editor.frameId;
25822         var etb = this;
25823         function btn(id, toggle, handler){
25824             var xid = fid + '-'+ id ;
25825             return {
25826                 id : xid,
25827                 cmd : id,
25828                 cls : 'x-btn-icon x-edit-'+id,
25829                 enableToggle:toggle !== false,
25830                 scope: editor, // was editor...
25831                 handler:handler||editor.relayBtnCmd,
25832                 clickEvent:'mousedown',
25833                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25834                 tabIndex:-1
25835             };
25836         }
25837         // create a new element.
25838         var wdiv = editor.wrap.createChild({
25839                 tag: 'div'
25840             }, editor.wrap.dom.firstChild.nextSibling, true);
25841         
25842         // can we do this more than once??
25843         
25844          // stop form submits
25845       
25846  
25847         // disable everything...
25848         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25849         this.toolbars = {};
25850            
25851         for (var i in  ty) {
25852           
25853             this.toolbars[i] = this.buildToolbar(ty[i],i);
25854         }
25855         this.tb = this.toolbars.BODY;
25856         this.tb.el.show();
25857         
25858          
25859         this.rendered = true;
25860         
25861         // the all the btns;
25862         editor.on('editorevent', this.updateToolbar, this);
25863         // other toolbars need to implement this..
25864         //editor.on('editmodechange', this.updateToolbar, this);
25865     },
25866     
25867     
25868     
25869     /**
25870      * Protected method that will not generally be called directly. It triggers
25871      * a toolbar update by reading the markup state of the current selection in the editor.
25872      */
25873     updateToolbar: function(){
25874
25875         if(!this.editor.activated){
25876             this.editor.onFirstFocus();
25877             return;
25878         }
25879
25880         
25881         var ans = this.editor.getAllAncestors();
25882         
25883         // pick
25884         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25885         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25886         sel = sel ? sel : this.editor.doc.body;
25887         sel = sel.tagName.length ? sel : this.editor.doc.body;
25888         var tn = sel.tagName.toUpperCase();
25889         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25890         tn = sel.tagName.toUpperCase();
25891         if (this.tb.name  == tn) {
25892             return; // no change
25893         }
25894         this.tb.el.hide();
25895         ///console.log("show: " + tn);
25896         this.tb =  this.toolbars[tn];
25897         this.tb.el.show();
25898         this.tb.fields.each(function(e) {
25899             e.setValue(sel.getAttribute(e.name));
25900         });
25901         this.tb.selectedNode = sel;
25902         
25903         
25904         Roo.menu.MenuMgr.hideAll();
25905
25906         //this.editorsyncValue();
25907     },
25908    
25909        
25910     // private
25911     onDestroy : function(){
25912         if(this.rendered){
25913             
25914             this.tb.items.each(function(item){
25915                 if(item.menu){
25916                     item.menu.removeAll();
25917                     if(item.menu.el){
25918                         item.menu.el.destroy();
25919                     }
25920                 }
25921                 item.destroy();
25922             });
25923              
25924         }
25925     },
25926     onFirstFocus: function() {
25927         // need to do this for all the toolbars..
25928         this.tb.items.each(function(item){
25929            item.enable();
25930         });
25931     },
25932     buildToolbar: function(tlist, nm)
25933     {
25934         var editor = this.editor;
25935          // create a new element.
25936         var wdiv = editor.wrap.createChild({
25937                 tag: 'div'
25938             }, editor.wrap.dom.firstChild.nextSibling, true);
25939         
25940        
25941         var tb = new Roo.Toolbar(wdiv);
25942         tb.add(nm+ ":&nbsp;");
25943         for (var i in tlist) {
25944             var item = tlist[i];
25945             tb.add(item.title + ":&nbsp;");
25946             if (item.opts) {
25947                 // fixme
25948                 
25949               
25950                 tb.addField( new Roo.form.ComboBox({
25951                     store: new Roo.data.SimpleStore({
25952                         id : 'val',
25953                         fields: ['val'],
25954                         data : item.opts // from states.js
25955                     }),
25956                     name : i,
25957                     displayField:'val',
25958                     typeAhead: false,
25959                     mode: 'local',
25960                     editable : false,
25961                     triggerAction: 'all',
25962                     emptyText:'Select',
25963                     selectOnFocus:true,
25964                     width: item.width ? item.width  : 130,
25965                     listeners : {
25966                         'select': function(c, r, i) {
25967                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25968                         }
25969                     }
25970
25971                 }));
25972                 continue;
25973                     
25974                 
25975                 
25976                 
25977                 
25978                 tb.addField( new Roo.form.TextField({
25979                     name: i,
25980                     width: 100,
25981                     //allowBlank:false,
25982                     value: ''
25983                 }));
25984                 continue;
25985             }
25986             tb.addField( new Roo.form.TextField({
25987                 name: i,
25988                 width: item.width,
25989                 //allowBlank:true,
25990                 value: '',
25991                 listeners: {
25992                     'change' : function(f, nv, ov) {
25993                         tb.selectedNode.setAttribute(f.name, nv);
25994                     }
25995                 }
25996             }));
25997              
25998         }
25999         tb.el.on('click', function(e){
26000             e.preventDefault(); // what does this do?
26001         });
26002         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26003         tb.el.hide();
26004         tb.name = nm;
26005         // dont need to disable them... as they will get hidden
26006         return tb;
26007          
26008         
26009     }
26010     
26011     
26012     
26013     
26014 });
26015
26016
26017
26018
26019
26020 /*
26021  * Based on:
26022  * Ext JS Library 1.1.1
26023  * Copyright(c) 2006-2007, Ext JS, LLC.
26024  *
26025  * Originally Released Under LGPL - original licence link has changed is not relivant.
26026  *
26027  * Fork - LGPL
26028  * <script type="text/javascript">
26029  */
26030  
26031 /**
26032  * @class Roo.form.BasicForm
26033  * @extends Roo.util.Observable
26034  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26035  * @constructor
26036  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26037  * @param {Object} config Configuration options
26038  */
26039 Roo.form.BasicForm = function(el, config){
26040     this.allItems = [];
26041     this.childForms = [];
26042     Roo.apply(this, config);
26043     /*
26044      * The Roo.form.Field items in this form.
26045      * @type MixedCollection
26046      */
26047      
26048      
26049     this.items = new Roo.util.MixedCollection(false, function(o){
26050         return o.id || (o.id = Roo.id());
26051     });
26052     this.addEvents({
26053         /**
26054          * @event beforeaction
26055          * Fires before any action is performed. Return false to cancel the action.
26056          * @param {Form} this
26057          * @param {Action} action The action to be performed
26058          */
26059         beforeaction: true,
26060         /**
26061          * @event actionfailed
26062          * Fires when an action fails.
26063          * @param {Form} this
26064          * @param {Action} action The action that failed
26065          */
26066         actionfailed : true,
26067         /**
26068          * @event actioncomplete
26069          * Fires when an action is completed.
26070          * @param {Form} this
26071          * @param {Action} action The action that completed
26072          */
26073         actioncomplete : true
26074     });
26075     if(el){
26076         this.initEl(el);
26077     }
26078     Roo.form.BasicForm.superclass.constructor.call(this);
26079 };
26080
26081 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26082     /**
26083      * @cfg {String} method
26084      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26085      */
26086     /**
26087      * @cfg {DataReader} reader
26088      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26089      * This is optional as there is built-in support for processing JSON.
26090      */
26091     /**
26092      * @cfg {DataReader} errorReader
26093      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26094      * This is completely optional as there is built-in support for processing JSON.
26095      */
26096     /**
26097      * @cfg {String} url
26098      * The URL to use for form actions if one isn't supplied in the action options.
26099      */
26100     /**
26101      * @cfg {Boolean} fileUpload
26102      * Set to true if this form is a file upload.
26103      */
26104      
26105     /**
26106      * @cfg {Object} baseParams
26107      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26108      */
26109      /**
26110      
26111     /**
26112      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26113      */
26114     timeout: 30,
26115
26116     // private
26117     activeAction : null,
26118
26119     /**
26120      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26121      * or setValues() data instead of when the form was first created.
26122      */
26123     trackResetOnLoad : false,
26124     
26125     
26126     /**
26127      * childForms - used for multi-tab forms
26128      * @type {Array}
26129      */
26130     childForms : false,
26131     
26132     /**
26133      * allItems - full list of fields.
26134      * @type {Array}
26135      */
26136     allItems : false,
26137     
26138     /**
26139      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26140      * element by passing it or its id or mask the form itself by passing in true.
26141      * @type Mixed
26142      */
26143     waitMsgTarget : false,
26144
26145     // private
26146     initEl : function(el){
26147         this.el = Roo.get(el);
26148         this.id = this.el.id || Roo.id();
26149         this.el.on('submit', this.onSubmit, this);
26150         this.el.addClass('x-form');
26151     },
26152
26153     // private
26154     onSubmit : function(e){
26155         e.stopEvent();
26156     },
26157
26158     /**
26159      * Returns true if client-side validation on the form is successful.
26160      * @return Boolean
26161      */
26162     isValid : function(){
26163         var valid = true;
26164         this.items.each(function(f){
26165            if(!f.validate()){
26166                valid = false;
26167            }
26168         });
26169         return valid;
26170     },
26171
26172     /**
26173      * Returns true if any fields in this form have changed since their original load.
26174      * @return Boolean
26175      */
26176     isDirty : function(){
26177         var dirty = false;
26178         this.items.each(function(f){
26179            if(f.isDirty()){
26180                dirty = true;
26181                return false;
26182            }
26183         });
26184         return dirty;
26185     },
26186
26187     /**
26188      * Performs a predefined action (submit or load) or custom actions you define on this form.
26189      * @param {String} actionName The name of the action type
26190      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26191      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26192      * accept other config options):
26193      * <pre>
26194 Property          Type             Description
26195 ----------------  ---------------  ----------------------------------------------------------------------------------
26196 url               String           The url for the action (defaults to the form's url)
26197 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26198 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26199 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26200                                    validate the form on the client (defaults to false)
26201      * </pre>
26202      * @return {BasicForm} this
26203      */
26204     doAction : function(action, options){
26205         if(typeof action == 'string'){
26206             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26207         }
26208         if(this.fireEvent('beforeaction', this, action) !== false){
26209             this.beforeAction(action);
26210             action.run.defer(100, action);
26211         }
26212         return this;
26213     },
26214
26215     /**
26216      * Shortcut to do a submit action.
26217      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26218      * @return {BasicForm} this
26219      */
26220     submit : function(options){
26221         this.doAction('submit', options);
26222         return this;
26223     },
26224
26225     /**
26226      * Shortcut to do a load action.
26227      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26228      * @return {BasicForm} this
26229      */
26230     load : function(options){
26231         this.doAction('load', options);
26232         return this;
26233     },
26234
26235     /**
26236      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26237      * @param {Record} record The record to edit
26238      * @return {BasicForm} this
26239      */
26240     updateRecord : function(record){
26241         record.beginEdit();
26242         var fs = record.fields;
26243         fs.each(function(f){
26244             var field = this.findField(f.name);
26245             if(field){
26246                 record.set(f.name, field.getValue());
26247             }
26248         }, this);
26249         record.endEdit();
26250         return this;
26251     },
26252
26253     /**
26254      * Loads an Roo.data.Record into this form.
26255      * @param {Record} record The record to load
26256      * @return {BasicForm} this
26257      */
26258     loadRecord : function(record){
26259         this.setValues(record.data);
26260         return this;
26261     },
26262
26263     // private
26264     beforeAction : function(action){
26265         var o = action.options;
26266         
26267        
26268         if(this.waitMsgTarget === true){
26269             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26270         }else if(this.waitMsgTarget){
26271             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26272             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26273         }else {
26274             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26275         }
26276          
26277     },
26278
26279     // private
26280     afterAction : function(action, success){
26281         this.activeAction = null;
26282         var o = action.options;
26283         
26284         if(this.waitMsgTarget === true){
26285             this.el.unmask();
26286         }else if(this.waitMsgTarget){
26287             this.waitMsgTarget.unmask();
26288         }else{
26289             Roo.MessageBox.updateProgress(1);
26290             Roo.MessageBox.hide();
26291         }
26292          
26293         if(success){
26294             if(o.reset){
26295                 this.reset();
26296             }
26297             Roo.callback(o.success, o.scope, [this, action]);
26298             this.fireEvent('actioncomplete', this, action);
26299             
26300         }else{
26301             Roo.callback(o.failure, o.scope, [this, action]);
26302             // show an error message if no failed handler is set..
26303             if (!this.hasListener('actionfailed')) {
26304                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26305             }
26306             
26307             this.fireEvent('actionfailed', this, action);
26308         }
26309         
26310     },
26311
26312     /**
26313      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26314      * @param {String} id The value to search for
26315      * @return Field
26316      */
26317     findField : function(id){
26318         var field = this.items.get(id);
26319         if(!field){
26320             this.items.each(function(f){
26321                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26322                     field = f;
26323                     return false;
26324                 }
26325             });
26326         }
26327         return field || null;
26328     },
26329
26330     /**
26331      * Add a secondary form to this one, 
26332      * Used to provide tabbed forms. One form is primary, with hidden values 
26333      * which mirror the elements from the other forms.
26334      * 
26335      * @param {Roo.form.Form} form to add.
26336      * 
26337      */
26338     addForm : function(form)
26339     {
26340        
26341         if (this.childForms.indexOf(form) > -1) {
26342             // already added..
26343             return;
26344         }
26345         this.childForms.push(form);
26346         var n = '';
26347         Roo.each(form.allItems, function (fe) {
26348             
26349             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26350             if (this.findField(n)) { // already added..
26351                 return;
26352             }
26353             var add = new Roo.form.Hidden({
26354                 name : n
26355             });
26356             add.render(this.el);
26357             
26358             this.add( add );
26359         }, this);
26360         
26361     },
26362     /**
26363      * Mark fields in this form invalid in bulk.
26364      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26365      * @return {BasicForm} this
26366      */
26367     markInvalid : function(errors){
26368         if(errors instanceof Array){
26369             for(var i = 0, len = errors.length; i < len; i++){
26370                 var fieldError = errors[i];
26371                 var f = this.findField(fieldError.id);
26372                 if(f){
26373                     f.markInvalid(fieldError.msg);
26374                 }
26375             }
26376         }else{
26377             var field, id;
26378             for(id in errors){
26379                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26380                     field.markInvalid(errors[id]);
26381                 }
26382             }
26383         }
26384         Roo.each(this.childForms || [], function (f) {
26385             f.markInvalid(errors);
26386         });
26387         
26388         return this;
26389     },
26390
26391     /**
26392      * Set values for fields in this form in bulk.
26393      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26394      * @return {BasicForm} this
26395      */
26396     setValues : function(values){
26397         if(values instanceof Array){ // array of objects
26398             for(var i = 0, len = values.length; i < len; i++){
26399                 var v = values[i];
26400                 var f = this.findField(v.id);
26401                 if(f){
26402                     f.setValue(v.value);
26403                     if(this.trackResetOnLoad){
26404                         f.originalValue = f.getValue();
26405                     }
26406                 }
26407             }
26408         }else{ // object hash
26409             var field, id;
26410             for(id in values){
26411                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26412                     
26413                     if (field.setFromData && 
26414                         field.valueField && 
26415                         field.displayField &&
26416                         // combos' with local stores can 
26417                         // be queried via setValue()
26418                         // to set their value..
26419                         (field.store && !field.store.isLocal)
26420                         ) {
26421                         // it's a combo
26422                         var sd = { };
26423                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26424                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26425                         field.setFromData(sd);
26426                         
26427                     } else {
26428                         field.setValue(values[id]);
26429                     }
26430                     
26431                     
26432                     if(this.trackResetOnLoad){
26433                         field.originalValue = field.getValue();
26434                     }
26435                 }
26436             }
26437         }
26438          
26439         Roo.each(this.childForms || [], function (f) {
26440             f.setValues(values);
26441         });
26442                 
26443         return this;
26444     },
26445
26446     /**
26447      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26448      * they are returned as an array.
26449      * @param {Boolean} asString
26450      * @return {Object}
26451      */
26452     getValues : function(asString){
26453         if (this.childForms) {
26454             // copy values from the child forms
26455             Roo.each(this.childForms, function (f) {
26456                 this.setValues(f.getValues());
26457             }, this);
26458         }
26459         
26460         
26461         
26462         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26463         if(asString === true){
26464             return fs;
26465         }
26466         return Roo.urlDecode(fs);
26467     },
26468     
26469     /**
26470      * Returns the fields in this form as an object with key/value pairs. 
26471      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26472      * @return {Object}
26473      */
26474     getFieldValues : function()
26475     {
26476         if (this.childForms) {
26477             // copy values from the child forms
26478             Roo.each(this.childForms, function (f) {
26479                 this.setValues(f.getValues());
26480             }, this);
26481         }
26482         
26483         var ret = {};
26484         this.items.each(function(f){
26485             if (!f.getName()) {
26486                 return;
26487             }
26488             var v = f.getValue();
26489             if ((typeof(v) == 'object') && f.getRawValue) {
26490                 v = f.getRawValue() ; // dates..
26491             }
26492             ret[f.getName()] = v;
26493         });
26494         
26495         return ret;
26496     },
26497
26498     /**
26499      * Clears all invalid messages in this form.
26500      * @return {BasicForm} this
26501      */
26502     clearInvalid : function(){
26503         this.items.each(function(f){
26504            f.clearInvalid();
26505         });
26506         
26507         Roo.each(this.childForms || [], function (f) {
26508             f.clearInvalid();
26509         });
26510         
26511         
26512         return this;
26513     },
26514
26515     /**
26516      * Resets this form.
26517      * @return {BasicForm} this
26518      */
26519     reset : function(){
26520         this.items.each(function(f){
26521             f.reset();
26522         });
26523         
26524         Roo.each(this.childForms || [], function (f) {
26525             f.reset();
26526         });
26527        
26528         
26529         return this;
26530     },
26531
26532     /**
26533      * Add Roo.form components to this form.
26534      * @param {Field} field1
26535      * @param {Field} field2 (optional)
26536      * @param {Field} etc (optional)
26537      * @return {BasicForm} this
26538      */
26539     add : function(){
26540         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26541         return this;
26542     },
26543
26544
26545     /**
26546      * Removes a field from the items collection (does NOT remove its markup).
26547      * @param {Field} field
26548      * @return {BasicForm} this
26549      */
26550     remove : function(field){
26551         this.items.remove(field);
26552         return this;
26553     },
26554
26555     /**
26556      * Looks at the fields in this form, checks them for an id attribute,
26557      * and calls applyTo on the existing dom element with that id.
26558      * @return {BasicForm} this
26559      */
26560     render : function(){
26561         this.items.each(function(f){
26562             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26563                 f.applyTo(f.id);
26564             }
26565         });
26566         return this;
26567     },
26568
26569     /**
26570      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26571      * @param {Object} values
26572      * @return {BasicForm} this
26573      */
26574     applyToFields : function(o){
26575         this.items.each(function(f){
26576            Roo.apply(f, o);
26577         });
26578         return this;
26579     },
26580
26581     /**
26582      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26583      * @param {Object} values
26584      * @return {BasicForm} this
26585      */
26586     applyIfToFields : function(o){
26587         this.items.each(function(f){
26588            Roo.applyIf(f, o);
26589         });
26590         return this;
26591     }
26592 });
26593
26594 // back compat
26595 Roo.BasicForm = Roo.form.BasicForm;/*
26596  * Based on:
26597  * Ext JS Library 1.1.1
26598  * Copyright(c) 2006-2007, Ext JS, LLC.
26599  *
26600  * Originally Released Under LGPL - original licence link has changed is not relivant.
26601  *
26602  * Fork - LGPL
26603  * <script type="text/javascript">
26604  */
26605
26606 /**
26607  * @class Roo.form.Form
26608  * @extends Roo.form.BasicForm
26609  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26610  * @constructor
26611  * @param {Object} config Configuration options
26612  */
26613 Roo.form.Form = function(config){
26614     var xitems =  [];
26615     if (config.items) {
26616         xitems = config.items;
26617         delete config.items;
26618     }
26619    
26620     
26621     Roo.form.Form.superclass.constructor.call(this, null, config);
26622     this.url = this.url || this.action;
26623     if(!this.root){
26624         this.root = new Roo.form.Layout(Roo.applyIf({
26625             id: Roo.id()
26626         }, config));
26627     }
26628     this.active = this.root;
26629     /**
26630      * Array of all the buttons that have been added to this form via {@link addButton}
26631      * @type Array
26632      */
26633     this.buttons = [];
26634     this.allItems = [];
26635     this.addEvents({
26636         /**
26637          * @event clientvalidation
26638          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26639          * @param {Form} this
26640          * @param {Boolean} valid true if the form has passed client-side validation
26641          */
26642         clientvalidation: true,
26643         /**
26644          * @event rendered
26645          * Fires when the form is rendered
26646          * @param {Roo.form.Form} form
26647          */
26648         rendered : true
26649     });
26650     
26651     if (this.progressUrl) {
26652             // push a hidden field onto the list of fields..
26653             this.addxtype( {
26654                     xns: Roo.form, 
26655                     xtype : 'Hidden', 
26656                     name : 'UPLOAD_IDENTIFIER' 
26657             });
26658         }
26659         
26660     
26661     Roo.each(xitems, this.addxtype, this);
26662     
26663     
26664     
26665 };
26666
26667 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26668     /**
26669      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26670      */
26671     /**
26672      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26673      */
26674     /**
26675      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26676      */
26677     buttonAlign:'center',
26678
26679     /**
26680      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26681      */
26682     minButtonWidth:75,
26683
26684     /**
26685      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26686      * This property cascades to child containers if not set.
26687      */
26688     labelAlign:'left',
26689
26690     /**
26691      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26692      * fires a looping event with that state. This is required to bind buttons to the valid
26693      * state using the config value formBind:true on the button.
26694      */
26695     monitorValid : false,
26696
26697     /**
26698      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26699      */
26700     monitorPoll : 200,
26701     
26702     /**
26703      * @cfg {String} progressUrl - Url to return progress data 
26704      */
26705     
26706     progressUrl : false,
26707   
26708     /**
26709      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26710      * fields are added and the column is closed. If no fields are passed the column remains open
26711      * until end() is called.
26712      * @param {Object} config The config to pass to the column
26713      * @param {Field} field1 (optional)
26714      * @param {Field} field2 (optional)
26715      * @param {Field} etc (optional)
26716      * @return Column The column container object
26717      */
26718     column : function(c){
26719         var col = new Roo.form.Column(c);
26720         this.start(col);
26721         if(arguments.length > 1){ // duplicate code required because of Opera
26722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26723             this.end();
26724         }
26725         return col;
26726     },
26727
26728     /**
26729      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26730      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26731      * until end() is called.
26732      * @param {Object} config The config to pass to the fieldset
26733      * @param {Field} field1 (optional)
26734      * @param {Field} field2 (optional)
26735      * @param {Field} etc (optional)
26736      * @return FieldSet The fieldset container object
26737      */
26738     fieldset : function(c){
26739         var fs = new Roo.form.FieldSet(c);
26740         this.start(fs);
26741         if(arguments.length > 1){ // duplicate code required because of Opera
26742             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26743             this.end();
26744         }
26745         return fs;
26746     },
26747
26748     /**
26749      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26750      * fields are added and the container is closed. If no fields are passed the container remains open
26751      * until end() is called.
26752      * @param {Object} config The config to pass to the Layout
26753      * @param {Field} field1 (optional)
26754      * @param {Field} field2 (optional)
26755      * @param {Field} etc (optional)
26756      * @return Layout The container object
26757      */
26758     container : function(c){
26759         var l = new Roo.form.Layout(c);
26760         this.start(l);
26761         if(arguments.length > 1){ // duplicate code required because of Opera
26762             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26763             this.end();
26764         }
26765         return l;
26766     },
26767
26768     /**
26769      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26770      * @param {Object} container A Roo.form.Layout or subclass of Layout
26771      * @return {Form} this
26772      */
26773     start : function(c){
26774         // cascade label info
26775         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26776         this.active.stack.push(c);
26777         c.ownerCt = this.active;
26778         this.active = c;
26779         return this;
26780     },
26781
26782     /**
26783      * Closes the current open container
26784      * @return {Form} this
26785      */
26786     end : function(){
26787         if(this.active == this.root){
26788             return this;
26789         }
26790         this.active = this.active.ownerCt;
26791         return this;
26792     },
26793
26794     /**
26795      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26796      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26797      * as the label of the field.
26798      * @param {Field} field1
26799      * @param {Field} field2 (optional)
26800      * @param {Field} etc. (optional)
26801      * @return {Form} this
26802      */
26803     add : function(){
26804         this.active.stack.push.apply(this.active.stack, arguments);
26805         this.allItems.push.apply(this.allItems,arguments);
26806         var r = [];
26807         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26808             if(a[i].isFormField){
26809                 r.push(a[i]);
26810             }
26811         }
26812         if(r.length > 0){
26813             Roo.form.Form.superclass.add.apply(this, r);
26814         }
26815         return this;
26816     },
26817     
26818
26819     
26820     
26821     
26822      /**
26823      * Find any element that has been added to a form, using it's ID or name
26824      * This can include framesets, columns etc. along with regular fields..
26825      * @param {String} id - id or name to find.
26826      
26827      * @return {Element} e - or false if nothing found.
26828      */
26829     findbyId : function(id)
26830     {
26831         var ret = false;
26832         if (!id) {
26833             return ret;
26834         }
26835         Roo.each(this.allItems, function(f){
26836             if (f.id == id || f.name == id ){
26837                 ret = f;
26838                 return false;
26839             }
26840         });
26841         return ret;
26842     },
26843
26844     
26845     
26846     /**
26847      * Render this form into the passed container. This should only be called once!
26848      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26849      * @return {Form} this
26850      */
26851     render : function(ct)
26852     {
26853         
26854         
26855         
26856         ct = Roo.get(ct);
26857         var o = this.autoCreate || {
26858             tag: 'form',
26859             method : this.method || 'POST',
26860             id : this.id || Roo.id()
26861         };
26862         this.initEl(ct.createChild(o));
26863
26864         this.root.render(this.el);
26865         
26866        
26867              
26868         this.items.each(function(f){
26869             f.render('x-form-el-'+f.id);
26870         });
26871
26872         if(this.buttons.length > 0){
26873             // tables are required to maintain order and for correct IE layout
26874             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26875                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26876                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26877             }}, null, true);
26878             var tr = tb.getElementsByTagName('tr')[0];
26879             for(var i = 0, len = this.buttons.length; i < len; i++) {
26880                 var b = this.buttons[i];
26881                 var td = document.createElement('td');
26882                 td.className = 'x-form-btn-td';
26883                 b.render(tr.appendChild(td));
26884             }
26885         }
26886         if(this.monitorValid){ // initialize after render
26887             this.startMonitoring();
26888         }
26889         this.fireEvent('rendered', this);
26890         return this;
26891     },
26892
26893     /**
26894      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26895      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26896      * object or a valid Roo.DomHelper element config
26897      * @param {Function} handler The function called when the button is clicked
26898      * @param {Object} scope (optional) The scope of the handler function
26899      * @return {Roo.Button}
26900      */
26901     addButton : function(config, handler, scope){
26902         var bc = {
26903             handler: handler,
26904             scope: scope,
26905             minWidth: this.minButtonWidth,
26906             hideParent:true
26907         };
26908         if(typeof config == "string"){
26909             bc.text = config;
26910         }else{
26911             Roo.apply(bc, config);
26912         }
26913         var btn = new Roo.Button(null, bc);
26914         this.buttons.push(btn);
26915         return btn;
26916     },
26917
26918      /**
26919      * Adds a series of form elements (using the xtype property as the factory method.
26920      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26921      * @param {Object} config 
26922      */
26923     
26924     addxtype : function()
26925     {
26926         var ar = Array.prototype.slice.call(arguments, 0);
26927         var ret = false;
26928         for(var i = 0; i < ar.length; i++) {
26929             if (!ar[i]) {
26930                 continue; // skip -- if this happends something invalid got sent, we 
26931                 // should ignore it, as basically that interface element will not show up
26932                 // and that should be pretty obvious!!
26933             }
26934             
26935             if (Roo.form[ar[i].xtype]) {
26936                 ar[i].form = this;
26937                 var fe = Roo.factory(ar[i], Roo.form);
26938                 if (!ret) {
26939                     ret = fe;
26940                 }
26941                 fe.form = this;
26942                 if (fe.store) {
26943                     fe.store.form = this;
26944                 }
26945                 if (fe.isLayout) {  
26946                          
26947                     this.start(fe);
26948                     this.allItems.push(fe);
26949                     if (fe.items && fe.addxtype) {
26950                         fe.addxtype.apply(fe, fe.items);
26951                         delete fe.items;
26952                     }
26953                      this.end();
26954                     continue;
26955                 }
26956                 
26957                 
26958                  
26959                 this.add(fe);
26960               //  console.log('adding ' + ar[i].xtype);
26961             }
26962             if (ar[i].xtype == 'Button') {  
26963                 //console.log('adding button');
26964                 //console.log(ar[i]);
26965                 this.addButton(ar[i]);
26966                 this.allItems.push(fe);
26967                 continue;
26968             }
26969             
26970             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26971                 alert('end is not supported on xtype any more, use items');
26972             //    this.end();
26973             //    //console.log('adding end');
26974             }
26975             
26976         }
26977         return ret;
26978     },
26979     
26980     /**
26981      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26982      * option "monitorValid"
26983      */
26984     startMonitoring : function(){
26985         if(!this.bound){
26986             this.bound = true;
26987             Roo.TaskMgr.start({
26988                 run : this.bindHandler,
26989                 interval : this.monitorPoll || 200,
26990                 scope: this
26991             });
26992         }
26993     },
26994
26995     /**
26996      * Stops monitoring of the valid state of this form
26997      */
26998     stopMonitoring : function(){
26999         this.bound = false;
27000     },
27001
27002     // private
27003     bindHandler : function(){
27004         if(!this.bound){
27005             return false; // stops binding
27006         }
27007         var valid = true;
27008         this.items.each(function(f){
27009             if(!f.isValid(true)){
27010                 valid = false;
27011                 return false;
27012             }
27013         });
27014         for(var i = 0, len = this.buttons.length; i < len; i++){
27015             var btn = this.buttons[i];
27016             if(btn.formBind === true && btn.disabled === valid){
27017                 btn.setDisabled(!valid);
27018             }
27019         }
27020         this.fireEvent('clientvalidation', this, valid);
27021     }
27022     
27023     
27024     
27025     
27026     
27027     
27028     
27029     
27030 });
27031
27032
27033 // back compat
27034 Roo.Form = Roo.form.Form;
27035 /*
27036  * Based on:
27037  * Ext JS Library 1.1.1
27038  * Copyright(c) 2006-2007, Ext JS, LLC.
27039  *
27040  * Originally Released Under LGPL - original licence link has changed is not relivant.
27041  *
27042  * Fork - LGPL
27043  * <script type="text/javascript">
27044  */
27045  
27046  /**
27047  * @class Roo.form.Action
27048  * Internal Class used to handle form actions
27049  * @constructor
27050  * @param {Roo.form.BasicForm} el The form element or its id
27051  * @param {Object} config Configuration options
27052  */
27053  
27054  
27055 // define the action interface
27056 Roo.form.Action = function(form, options){
27057     this.form = form;
27058     this.options = options || {};
27059 };
27060 /**
27061  * Client Validation Failed
27062  * @const 
27063  */
27064 Roo.form.Action.CLIENT_INVALID = 'client';
27065 /**
27066  * Server Validation Failed
27067  * @const 
27068  */
27069  Roo.form.Action.SERVER_INVALID = 'server';
27070  /**
27071  * Connect to Server Failed
27072  * @const 
27073  */
27074 Roo.form.Action.CONNECT_FAILURE = 'connect';
27075 /**
27076  * Reading Data from Server Failed
27077  * @const 
27078  */
27079 Roo.form.Action.LOAD_FAILURE = 'load';
27080
27081 Roo.form.Action.prototype = {
27082     type : 'default',
27083     failureType : undefined,
27084     response : undefined,
27085     result : undefined,
27086
27087     // interface method
27088     run : function(options){
27089
27090     },
27091
27092     // interface method
27093     success : function(response){
27094
27095     },
27096
27097     // interface method
27098     handleResponse : function(response){
27099
27100     },
27101
27102     // default connection failure
27103     failure : function(response){
27104         
27105         this.response = response;
27106         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27107         this.form.afterAction(this, false);
27108     },
27109
27110     processResponse : function(response){
27111         this.response = response;
27112         if(!response.responseText){
27113             return true;
27114         }
27115         this.result = this.handleResponse(response);
27116         return this.result;
27117     },
27118
27119     // utility functions used internally
27120     getUrl : function(appendParams){
27121         var url = this.options.url || this.form.url || this.form.el.dom.action;
27122         if(appendParams){
27123             var p = this.getParams();
27124             if(p){
27125                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27126             }
27127         }
27128         return url;
27129     },
27130
27131     getMethod : function(){
27132         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27133     },
27134
27135     getParams : function(){
27136         var bp = this.form.baseParams;
27137         var p = this.options.params;
27138         if(p){
27139             if(typeof p == "object"){
27140                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27141             }else if(typeof p == 'string' && bp){
27142                 p += '&' + Roo.urlEncode(bp);
27143             }
27144         }else if(bp){
27145             p = Roo.urlEncode(bp);
27146         }
27147         return p;
27148     },
27149
27150     createCallback : function(){
27151         return {
27152             success: this.success,
27153             failure: this.failure,
27154             scope: this,
27155             timeout: (this.form.timeout*1000),
27156             upload: this.form.fileUpload ? this.success : undefined
27157         };
27158     }
27159 };
27160
27161 Roo.form.Action.Submit = function(form, options){
27162     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27163 };
27164
27165 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27166     type : 'submit',
27167
27168     haveProgress : false,
27169     uploadComplete : false,
27170     
27171     // uploadProgress indicator.
27172     uploadProgress : function()
27173     {
27174         if (!this.form.progressUrl) {
27175             return;
27176         }
27177         
27178         if (!this.haveProgress) {
27179             Roo.MessageBox.progress("Uploading", "Uploading");
27180         }
27181         if (this.uploadComplete) {
27182            Roo.MessageBox.hide();
27183            return;
27184         }
27185         
27186         this.haveProgress = true;
27187    
27188         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27189         
27190         var c = new Roo.data.Connection();
27191         c.request({
27192             url : this.form.progressUrl,
27193             params: {
27194                 id : uid
27195             },
27196             method: 'GET',
27197             success : function(req){
27198                //console.log(data);
27199                 var rdata = false;
27200                 var edata;
27201                 try  {
27202                    rdata = Roo.decode(req.responseText)
27203                 } catch (e) {
27204                     Roo.log("Invalid data from server..");
27205                     Roo.log(edata);
27206                     return;
27207                 }
27208                 if (!rdata || !rdata.success) {
27209                     Roo.log(rdata);
27210                     return;
27211                 }
27212                 var data = rdata.data;
27213                 
27214                 if (this.uploadComplete) {
27215                    Roo.MessageBox.hide();
27216                    return;
27217                 }
27218                    
27219                 if (data){
27220                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27221                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27222                     );
27223                 }
27224                 this.uploadProgress.defer(2000,this);
27225             },
27226        
27227             failure: function(data) {
27228                 Roo.log('progress url failed ');
27229                 Roo.log(data);
27230             },
27231             scope : this
27232         });
27233            
27234     },
27235     
27236     
27237     run : function()
27238     {
27239         // run get Values on the form, so it syncs any secondary forms.
27240         this.form.getValues();
27241         
27242         var o = this.options;
27243         var method = this.getMethod();
27244         var isPost = method == 'POST';
27245         if(o.clientValidation === false || this.form.isValid()){
27246             
27247             if (this.form.progressUrl) {
27248                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27249                     (new Date() * 1) + '' + Math.random());
27250                     
27251             } 
27252             
27253             
27254             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27255                 form:this.form.el.dom,
27256                 url:this.getUrl(!isPost),
27257                 method: method,
27258                 params:isPost ? this.getParams() : null,
27259                 isUpload: this.form.fileUpload
27260             }));
27261             
27262             this.uploadProgress();
27263
27264         }else if (o.clientValidation !== false){ // client validation failed
27265             this.failureType = Roo.form.Action.CLIENT_INVALID;
27266             this.form.afterAction(this, false);
27267         }
27268     },
27269
27270     success : function(response)
27271     {
27272         this.uploadComplete= true;
27273         if (this.haveProgress) {
27274             Roo.MessageBox.hide();
27275         }
27276         
27277         
27278         var result = this.processResponse(response);
27279         if(result === true || result.success){
27280             this.form.afterAction(this, true);
27281             return;
27282         }
27283         if(result.errors){
27284             this.form.markInvalid(result.errors);
27285             this.failureType = Roo.form.Action.SERVER_INVALID;
27286         }
27287         this.form.afterAction(this, false);
27288     },
27289     failure : function(response)
27290     {
27291         this.uploadComplete= true;
27292         if (this.haveProgress) {
27293             Roo.MessageBox.hide();
27294         }
27295         
27296         
27297         this.response = response;
27298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27299         this.form.afterAction(this, false);
27300     },
27301     
27302     handleResponse : function(response){
27303         if(this.form.errorReader){
27304             var rs = this.form.errorReader.read(response);
27305             var errors = [];
27306             if(rs.records){
27307                 for(var i = 0, len = rs.records.length; i < len; i++) {
27308                     var r = rs.records[i];
27309                     errors[i] = r.data;
27310                 }
27311             }
27312             if(errors.length < 1){
27313                 errors = null;
27314             }
27315             return {
27316                 success : rs.success,
27317                 errors : errors
27318             };
27319         }
27320         var ret = false;
27321         try {
27322             ret = Roo.decode(response.responseText);
27323         } catch (e) {
27324             ret = {
27325                 success: false,
27326                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27327                 errors : []
27328             };
27329         }
27330         return ret;
27331         
27332     }
27333 });
27334
27335
27336 Roo.form.Action.Load = function(form, options){
27337     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27338     this.reader = this.form.reader;
27339 };
27340
27341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27342     type : 'load',
27343
27344     run : function(){
27345         
27346         Roo.Ajax.request(Roo.apply(
27347                 this.createCallback(), {
27348                     method:this.getMethod(),
27349                     url:this.getUrl(false),
27350                     params:this.getParams()
27351         }));
27352     },
27353
27354     success : function(response){
27355         
27356         var result = this.processResponse(response);
27357         if(result === true || !result.success || !result.data){
27358             this.failureType = Roo.form.Action.LOAD_FAILURE;
27359             this.form.afterAction(this, false);
27360             return;
27361         }
27362         this.form.clearInvalid();
27363         this.form.setValues(result.data);
27364         this.form.afterAction(this, true);
27365     },
27366
27367     handleResponse : function(response){
27368         if(this.form.reader){
27369             var rs = this.form.reader.read(response);
27370             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27371             return {
27372                 success : rs.success,
27373                 data : data
27374             };
27375         }
27376         return Roo.decode(response.responseText);
27377     }
27378 });
27379
27380 Roo.form.Action.ACTION_TYPES = {
27381     'load' : Roo.form.Action.Load,
27382     'submit' : Roo.form.Action.Submit
27383 };/*
27384  * Based on:
27385  * Ext JS Library 1.1.1
27386  * Copyright(c) 2006-2007, Ext JS, LLC.
27387  *
27388  * Originally Released Under LGPL - original licence link has changed is not relivant.
27389  *
27390  * Fork - LGPL
27391  * <script type="text/javascript">
27392  */
27393  
27394 /**
27395  * @class Roo.form.Layout
27396  * @extends Roo.Component
27397  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27398  * @constructor
27399  * @param {Object} config Configuration options
27400  */
27401 Roo.form.Layout = function(config){
27402     var xitems = [];
27403     if (config.items) {
27404         xitems = config.items;
27405         delete config.items;
27406     }
27407     Roo.form.Layout.superclass.constructor.call(this, config);
27408     this.stack = [];
27409     Roo.each(xitems, this.addxtype, this);
27410      
27411 };
27412
27413 Roo.extend(Roo.form.Layout, Roo.Component, {
27414     /**
27415      * @cfg {String/Object} autoCreate
27416      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27417      */
27418     /**
27419      * @cfg {String/Object/Function} style
27420      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27421      * a function which returns such a specification.
27422      */
27423     /**
27424      * @cfg {String} labelAlign
27425      * Valid values are "left," "top" and "right" (defaults to "left")
27426      */
27427     /**
27428      * @cfg {Number} labelWidth
27429      * Fixed width in pixels of all field labels (defaults to undefined)
27430      */
27431     /**
27432      * @cfg {Boolean} clear
27433      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27434      */
27435     clear : true,
27436     /**
27437      * @cfg {String} labelSeparator
27438      * The separator to use after field labels (defaults to ':')
27439      */
27440     labelSeparator : ':',
27441     /**
27442      * @cfg {Boolean} hideLabels
27443      * True to suppress the display of field labels in this layout (defaults to false)
27444      */
27445     hideLabels : false,
27446
27447     // private
27448     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27449     
27450     isLayout : true,
27451     
27452     // private
27453     onRender : function(ct, position){
27454         if(this.el){ // from markup
27455             this.el = Roo.get(this.el);
27456         }else {  // generate
27457             var cfg = this.getAutoCreate();
27458             this.el = ct.createChild(cfg, position);
27459         }
27460         if(this.style){
27461             this.el.applyStyles(this.style);
27462         }
27463         if(this.labelAlign){
27464             this.el.addClass('x-form-label-'+this.labelAlign);
27465         }
27466         if(this.hideLabels){
27467             this.labelStyle = "display:none";
27468             this.elementStyle = "padding-left:0;";
27469         }else{
27470             if(typeof this.labelWidth == 'number'){
27471                 this.labelStyle = "width:"+this.labelWidth+"px;";
27472                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27473             }
27474             if(this.labelAlign == 'top'){
27475                 this.labelStyle = "width:auto;";
27476                 this.elementStyle = "padding-left:0;";
27477             }
27478         }
27479         var stack = this.stack;
27480         var slen = stack.length;
27481         if(slen > 0){
27482             if(!this.fieldTpl){
27483                 var t = new Roo.Template(
27484                     '<div class="x-form-item {5}">',
27485                         '<label for="{0}" style="{2}">{1}{4}</label>',
27486                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27487                         '</div>',
27488                     '</div><div class="x-form-clear-left"></div>'
27489                 );
27490                 t.disableFormats = true;
27491                 t.compile();
27492                 Roo.form.Layout.prototype.fieldTpl = t;
27493             }
27494             for(var i = 0; i < slen; i++) {
27495                 if(stack[i].isFormField){
27496                     this.renderField(stack[i]);
27497                 }else{
27498                     this.renderComponent(stack[i]);
27499                 }
27500             }
27501         }
27502         if(this.clear){
27503             this.el.createChild({cls:'x-form-clear'});
27504         }
27505     },
27506
27507     // private
27508     renderField : function(f){
27509         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27510                f.id, //0
27511                f.fieldLabel, //1
27512                f.labelStyle||this.labelStyle||'', //2
27513                this.elementStyle||'', //3
27514                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27515                f.itemCls||this.itemCls||''  //5
27516        ], true).getPrevSibling());
27517     },
27518
27519     // private
27520     renderComponent : function(c){
27521         c.render(c.isLayout ? this.el : this.el.createChild());    
27522     },
27523     /**
27524      * Adds a object form elements (using the xtype property as the factory method.)
27525      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27526      * @param {Object} config 
27527      */
27528     addxtype : function(o)
27529     {
27530         // create the lement.
27531         o.form = this.form;
27532         var fe = Roo.factory(o, Roo.form);
27533         this.form.allItems.push(fe);
27534         this.stack.push(fe);
27535         
27536         if (fe.isFormField) {
27537             this.form.items.add(fe);
27538         }
27539          
27540         return fe;
27541     }
27542 });
27543
27544 /**
27545  * @class Roo.form.Column
27546  * @extends Roo.form.Layout
27547  * Creates a column 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.Column = function(config){
27552     Roo.form.Column.superclass.constructor.call(this, config);
27553 };
27554
27555 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27556     /**
27557      * @cfg {Number/String} width
27558      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27559      */
27560     /**
27561      * @cfg {String/Object} autoCreate
27562      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27563      */
27564
27565     // private
27566     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27567
27568     // private
27569     onRender : function(ct, position){
27570         Roo.form.Column.superclass.onRender.call(this, ct, position);
27571         if(this.width){
27572             this.el.setWidth(this.width);
27573         }
27574     }
27575 });
27576
27577
27578 /**
27579  * @class Roo.form.Row
27580  * @extends Roo.form.Layout
27581  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27582  * @constructor
27583  * @param {Object} config Configuration options
27584  */
27585
27586  
27587 Roo.form.Row = function(config){
27588     Roo.form.Row.superclass.constructor.call(this, config);
27589 };
27590  
27591 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27592       /**
27593      * @cfg {Number/String} width
27594      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27595      */
27596     /**
27597      * @cfg {Number/String} height
27598      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27599      */
27600     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27601     
27602     padWidth : 20,
27603     // private
27604     onRender : function(ct, position){
27605         //console.log('row render');
27606         if(!this.rowTpl){
27607             var t = new Roo.Template(
27608                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27609                     '<label for="{0}" style="{2}">{1}{4}</label>',
27610                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27611                     '</div>',
27612                 '</div>'
27613             );
27614             t.disableFormats = true;
27615             t.compile();
27616             Roo.form.Layout.prototype.rowTpl = t;
27617         }
27618         this.fieldTpl = this.rowTpl;
27619         
27620         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27621         var labelWidth = 100;
27622         
27623         if ((this.labelAlign != 'top')) {
27624             if (typeof this.labelWidth == 'number') {
27625                 labelWidth = this.labelWidth
27626             }
27627             this.padWidth =  20 + labelWidth;
27628             
27629         }
27630         
27631         Roo.form.Column.superclass.onRender.call(this, ct, position);
27632         if(this.width){
27633             this.el.setWidth(this.width);
27634         }
27635         if(this.height){
27636             this.el.setHeight(this.height);
27637         }
27638     },
27639     
27640     // private
27641     renderField : function(f){
27642         f.fieldEl = this.fieldTpl.append(this.el, [
27643                f.id, f.fieldLabel,
27644                f.labelStyle||this.labelStyle||'',
27645                this.elementStyle||'',
27646                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27647                f.itemCls||this.itemCls||'',
27648                f.width ? f.width + this.padWidth : 160 + this.padWidth
27649        ],true);
27650     }
27651 });
27652  
27653
27654 /**
27655  * @class Roo.form.FieldSet
27656  * @extends Roo.form.Layout
27657  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27658  * @constructor
27659  * @param {Object} config Configuration options
27660  */
27661 Roo.form.FieldSet = function(config){
27662     Roo.form.FieldSet.superclass.constructor.call(this, config);
27663 };
27664
27665 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27666     /**
27667      * @cfg {String} legend
27668      * The text to display as the legend for the FieldSet (defaults to '')
27669      */
27670     /**
27671      * @cfg {String/Object} autoCreate
27672      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27673      */
27674
27675     // private
27676     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27677
27678     // private
27679     onRender : function(ct, position){
27680         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27681         if(this.legend){
27682             this.setLegend(this.legend);
27683         }
27684     },
27685
27686     // private
27687     setLegend : function(text){
27688         if(this.rendered){
27689             this.el.child('legend').update(text);
27690         }
27691     }
27692 });/*
27693  * Based on:
27694  * Ext JS Library 1.1.1
27695  * Copyright(c) 2006-2007, Ext JS, LLC.
27696  *
27697  * Originally Released Under LGPL - original licence link has changed is not relivant.
27698  *
27699  * Fork - LGPL
27700  * <script type="text/javascript">
27701  */
27702 /**
27703  * @class Roo.form.VTypes
27704  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27705  * @singleton
27706  */
27707 Roo.form.VTypes = function(){
27708     // closure these in so they are only created once.
27709     var alpha = /^[a-zA-Z_]+$/;
27710     var alphanum = /^[a-zA-Z0-9_]+$/;
27711     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27712     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27713
27714     // All these messages and functions are configurable
27715     return {
27716         /**
27717          * The function used to validate email addresses
27718          * @param {String} value The email address
27719          */
27720         'email' : function(v){
27721             return email.test(v);
27722         },
27723         /**
27724          * The error text to display when the email validation function returns false
27725          * @type String
27726          */
27727         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27728         /**
27729          * The keystroke filter mask to be applied on email input
27730          * @type RegExp
27731          */
27732         'emailMask' : /[a-z0-9_\.\-@]/i,
27733
27734         /**
27735          * The function used to validate URLs
27736          * @param {String} value The URL
27737          */
27738         'url' : function(v){
27739             return url.test(v);
27740         },
27741         /**
27742          * The error text to display when the url validation function returns false
27743          * @type String
27744          */
27745         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27746         
27747         /**
27748          * The function used to validate alpha values
27749          * @param {String} value The value
27750          */
27751         'alpha' : function(v){
27752             return alpha.test(v);
27753         },
27754         /**
27755          * The error text to display when the alpha validation function returns false
27756          * @type String
27757          */
27758         'alphaText' : 'This field should only contain letters and _',
27759         /**
27760          * The keystroke filter mask to be applied on alpha input
27761          * @type RegExp
27762          */
27763         'alphaMask' : /[a-z_]/i,
27764
27765         /**
27766          * The function used to validate alphanumeric values
27767          * @param {String} value The value
27768          */
27769         'alphanum' : function(v){
27770             return alphanum.test(v);
27771         },
27772         /**
27773          * The error text to display when the alphanumeric validation function returns false
27774          * @type String
27775          */
27776         'alphanumText' : 'This field should only contain letters, numbers and _',
27777         /**
27778          * The keystroke filter mask to be applied on alphanumeric input
27779          * @type RegExp
27780          */
27781         'alphanumMask' : /[a-z0-9_]/i
27782     };
27783 }();//<script type="text/javascript">
27784
27785 /**
27786  * @class Roo.form.FCKeditor
27787  * @extends Roo.form.TextArea
27788  * Wrapper around the FCKEditor http://www.fckeditor.net
27789  * @constructor
27790  * Creates a new FCKeditor
27791  * @param {Object} config Configuration options
27792  */
27793 Roo.form.FCKeditor = function(config){
27794     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27795     this.addEvents({
27796          /**
27797          * @event editorinit
27798          * Fired when the editor is initialized - you can add extra handlers here..
27799          * @param {FCKeditor} this
27800          * @param {Object} the FCK object.
27801          */
27802         editorinit : true
27803     });
27804     
27805     
27806 };
27807 Roo.form.FCKeditor.editors = { };
27808 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27809 {
27810     //defaultAutoCreate : {
27811     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27812     //},
27813     // private
27814     /**
27815      * @cfg {Object} fck options - see fck manual for details.
27816      */
27817     fckconfig : false,
27818     
27819     /**
27820      * @cfg {Object} fck toolbar set (Basic or Default)
27821      */
27822     toolbarSet : 'Basic',
27823     /**
27824      * @cfg {Object} fck BasePath
27825      */ 
27826     basePath : '/fckeditor/',
27827     
27828     
27829     frame : false,
27830     
27831     value : '',
27832     
27833    
27834     onRender : function(ct, position)
27835     {
27836         if(!this.el){
27837             this.defaultAutoCreate = {
27838                 tag: "textarea",
27839                 style:"width:300px;height:60px;",
27840                 autocomplete: "off"
27841             };
27842         }
27843         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27844         /*
27845         if(this.grow){
27846             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27847             if(this.preventScrollbars){
27848                 this.el.setStyle("overflow", "hidden");
27849             }
27850             this.el.setHeight(this.growMin);
27851         }
27852         */
27853         //console.log('onrender' + this.getId() );
27854         Roo.form.FCKeditor.editors[this.getId()] = this;
27855          
27856
27857         this.replaceTextarea() ;
27858         
27859     },
27860     
27861     getEditor : function() {
27862         return this.fckEditor;
27863     },
27864     /**
27865      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27866      * @param {Mixed} value The value to set
27867      */
27868     
27869     
27870     setValue : function(value)
27871     {
27872         //console.log('setValue: ' + value);
27873         
27874         if(typeof(value) == 'undefined') { // not sure why this is happending...
27875             return;
27876         }
27877         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27878         
27879         //if(!this.el || !this.getEditor()) {
27880         //    this.value = value;
27881             //this.setValue.defer(100,this,[value]);    
27882         //    return;
27883         //} 
27884         
27885         if(!this.getEditor()) {
27886             return;
27887         }
27888         
27889         this.getEditor().SetData(value);
27890         
27891         //
27892
27893     },
27894
27895     /**
27896      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27897      * @return {Mixed} value The field value
27898      */
27899     getValue : function()
27900     {
27901         
27902         if (this.frame && this.frame.dom.style.display == 'none') {
27903             return Roo.form.FCKeditor.superclass.getValue.call(this);
27904         }
27905         
27906         if(!this.el || !this.getEditor()) {
27907            
27908            // this.getValue.defer(100,this); 
27909             return this.value;
27910         }
27911        
27912         
27913         var value=this.getEditor().GetData();
27914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27915         return Roo.form.FCKeditor.superclass.getValue.call(this);
27916         
27917
27918     },
27919
27920     /**
27921      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27922      * @return {Mixed} value The field value
27923      */
27924     getRawValue : function()
27925     {
27926         if (this.frame && this.frame.dom.style.display == 'none') {
27927             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27928         }
27929         
27930         if(!this.el || !this.getEditor()) {
27931             //this.getRawValue.defer(100,this); 
27932             return this.value;
27933             return;
27934         }
27935         
27936         
27937         
27938         var value=this.getEditor().GetData();
27939         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27940         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27941          
27942     },
27943     
27944     setSize : function(w,h) {
27945         
27946         
27947         
27948         //if (this.frame && this.frame.dom.style.display == 'none') {
27949         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27950         //    return;
27951         //}
27952         //if(!this.el || !this.getEditor()) {
27953         //    this.setSize.defer(100,this, [w,h]); 
27954         //    return;
27955         //}
27956         
27957         
27958         
27959         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27960         
27961         this.frame.dom.setAttribute('width', w);
27962         this.frame.dom.setAttribute('height', h);
27963         this.frame.setSize(w,h);
27964         
27965     },
27966     
27967     toggleSourceEdit : function(value) {
27968         
27969       
27970          
27971         this.el.dom.style.display = value ? '' : 'none';
27972         this.frame.dom.style.display = value ?  'none' : '';
27973         
27974     },
27975     
27976     
27977     focus: function(tag)
27978     {
27979         if (this.frame.dom.style.display == 'none') {
27980             return Roo.form.FCKeditor.superclass.focus.call(this);
27981         }
27982         if(!this.el || !this.getEditor()) {
27983             this.focus.defer(100,this, [tag]); 
27984             return;
27985         }
27986         
27987         
27988         
27989         
27990         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27991         this.getEditor().Focus();
27992         if (tgs.length) {
27993             if (!this.getEditor().Selection.GetSelection()) {
27994                 this.focus.defer(100,this, [tag]); 
27995                 return;
27996             }
27997             
27998             
27999             var r = this.getEditor().EditorDocument.createRange();
28000             r.setStart(tgs[0],0);
28001             r.setEnd(tgs[0],0);
28002             this.getEditor().Selection.GetSelection().removeAllRanges();
28003             this.getEditor().Selection.GetSelection().addRange(r);
28004             this.getEditor().Focus();
28005         }
28006         
28007     },
28008     
28009     
28010     
28011     replaceTextarea : function()
28012     {
28013         if ( document.getElementById( this.getId() + '___Frame' ) )
28014             return ;
28015         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28016         //{
28017             // We must check the elements firstly using the Id and then the name.
28018         var oTextarea = document.getElementById( this.getId() );
28019         
28020         var colElementsByName = document.getElementsByName( this.getId() ) ;
28021          
28022         oTextarea.style.display = 'none' ;
28023
28024         if ( oTextarea.tabIndex ) {            
28025             this.TabIndex = oTextarea.tabIndex ;
28026         }
28027         
28028         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28029         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28030         this.frame = Roo.get(this.getId() + '___Frame')
28031     },
28032     
28033     _getConfigHtml : function()
28034     {
28035         var sConfig = '' ;
28036
28037         for ( var o in this.fckconfig ) {
28038             sConfig += sConfig.length > 0  ? '&amp;' : '';
28039             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28040         }
28041
28042         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28043     },
28044     
28045     
28046     _getIFrameHtml : function()
28047     {
28048         var sFile = 'fckeditor.html' ;
28049         /* no idea what this is about..
28050         try
28051         {
28052             if ( (/fcksource=true/i).test( window.top.location.search ) )
28053                 sFile = 'fckeditor.original.html' ;
28054         }
28055         catch (e) { 
28056         */
28057
28058         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28059         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28060         
28061         
28062         var html = '<iframe id="' + this.getId() +
28063             '___Frame" src="' + sLink +
28064             '" width="' + this.width +
28065             '" height="' + this.height + '"' +
28066             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28067             ' frameborder="0" scrolling="no"></iframe>' ;
28068
28069         return html ;
28070     },
28071     
28072     _insertHtmlBefore : function( html, element )
28073     {
28074         if ( element.insertAdjacentHTML )       {
28075             // IE
28076             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28077         } else { // Gecko
28078             var oRange = document.createRange() ;
28079             oRange.setStartBefore( element ) ;
28080             var oFragment = oRange.createContextualFragment( html );
28081             element.parentNode.insertBefore( oFragment, element ) ;
28082         }
28083     }
28084     
28085     
28086   
28087     
28088     
28089     
28090     
28091
28092 });
28093
28094 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28095
28096 function FCKeditor_OnComplete(editorInstance){
28097     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28098     f.fckEditor = editorInstance;
28099     //console.log("loaded");
28100     f.fireEvent('editorinit', f, editorInstance);
28101
28102   
28103
28104  
28105
28106
28107
28108
28109
28110
28111
28112
28113
28114
28115
28116
28117
28118
28119
28120 //<script type="text/javascript">
28121 /**
28122  * @class Roo.form.GridField
28123  * @extends Roo.form.Field
28124  * Embed a grid (or editable grid into a form)
28125  * STATUS ALPHA
28126  * 
28127  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28128  * it needs 
28129  * xgrid.store = Roo.data.Store
28130  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28131  * xgrid.store.reader = Roo.data.JsonReader 
28132  * 
28133  * 
28134  * @constructor
28135  * Creates a new GridField
28136  * @param {Object} config Configuration options
28137  */
28138 Roo.form.GridField = function(config){
28139     Roo.form.GridField.superclass.constructor.call(this, config);
28140      
28141 };
28142
28143 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28144     /**
28145      * @cfg {Number} width  - used to restrict width of grid..
28146      */
28147     width : 100,
28148     /**
28149      * @cfg {Number} height - used to restrict height of grid..
28150      */
28151     height : 50,
28152      /**
28153      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28154          * 
28155          *}
28156      */
28157     xgrid : false, 
28158     /**
28159      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28160      * {tag: "input", type: "checkbox", autocomplete: "off"})
28161      */
28162    // defaultAutoCreate : { tag: 'div' },
28163     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28164     /**
28165      * @cfg {String} addTitle Text to include for adding a title.
28166      */
28167     addTitle : false,
28168     //
28169     onResize : function(){
28170         Roo.form.Field.superclass.onResize.apply(this, arguments);
28171     },
28172
28173     initEvents : function(){
28174         // Roo.form.Checkbox.superclass.initEvents.call(this);
28175         // has no events...
28176        
28177     },
28178
28179
28180     getResizeEl : function(){
28181         return this.wrap;
28182     },
28183
28184     getPositionEl : function(){
28185         return this.wrap;
28186     },
28187
28188     // private
28189     onRender : function(ct, position){
28190         
28191         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28192         var style = this.style;
28193         delete this.style;
28194         
28195         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28196         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28197         this.viewEl = this.wrap.createChild({ tag: 'div' });
28198         if (style) {
28199             this.viewEl.applyStyles(style);
28200         }
28201         if (this.width) {
28202             this.viewEl.setWidth(this.width);
28203         }
28204         if (this.height) {
28205             this.viewEl.setHeight(this.height);
28206         }
28207         //if(this.inputValue !== undefined){
28208         //this.setValue(this.value);
28209         
28210         
28211         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28212         
28213         
28214         this.grid.render();
28215         this.grid.getDataSource().on('remove', this.refreshValue, this);
28216         this.grid.getDataSource().on('update', this.refreshValue, this);
28217         this.grid.on('afteredit', this.refreshValue, this);
28218  
28219     },
28220      
28221     
28222     /**
28223      * Sets the value of the item. 
28224      * @param {String} either an object  or a string..
28225      */
28226     setValue : function(v){
28227         //this.value = v;
28228         v = v || []; // empty set..
28229         // this does not seem smart - it really only affects memoryproxy grids..
28230         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28231             var ds = this.grid.getDataSource();
28232             // assumes a json reader..
28233             var data = {}
28234             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28235             ds.loadData( data);
28236         }
28237         Roo.form.GridField.superclass.setValue.call(this, v);
28238         this.refreshValue();
28239         // should load data in the grid really....
28240     },
28241     
28242     // private
28243     refreshValue: function() {
28244          var val = [];
28245         this.grid.getDataSource().each(function(r) {
28246             val.push(r.data);
28247         });
28248         this.el.dom.value = Roo.encode(val);
28249     }
28250     
28251      
28252     
28253     
28254 });/*
28255  * Based on:
28256  * Ext JS Library 1.1.1
28257  * Copyright(c) 2006-2007, Ext JS, LLC.
28258  *
28259  * Originally Released Under LGPL - original licence link has changed is not relivant.
28260  *
28261  * Fork - LGPL
28262  * <script type="text/javascript">
28263  */
28264 /**
28265  * @class Roo.form.DisplayField
28266  * @extends Roo.form.Field
28267  * A generic Field to display non-editable data.
28268  * @constructor
28269  * Creates a new Display Field item.
28270  * @param {Object} config Configuration options
28271  */
28272 Roo.form.DisplayField = function(config){
28273     Roo.form.DisplayField.superclass.constructor.call(this, config);
28274     
28275 };
28276
28277 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28278     inputType:      'hidden',
28279     allowBlank:     true,
28280     readOnly:         true,
28281     
28282  
28283     /**
28284      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28285      */
28286     focusClass : undefined,
28287     /**
28288      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28289      */
28290     fieldClass: 'x-form-field',
28291     
28292      /**
28293      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28294      */
28295     valueRenderer: undefined,
28296     
28297     width: 100,
28298     /**
28299      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28300      * {tag: "input", type: "checkbox", autocomplete: "off"})
28301      */
28302      
28303  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28304
28305     onResize : function(){
28306         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28307         
28308     },
28309
28310     initEvents : function(){
28311         // Roo.form.Checkbox.superclass.initEvents.call(this);
28312         // has no events...
28313        
28314     },
28315
28316
28317     getResizeEl : function(){
28318         return this.wrap;
28319     },
28320
28321     getPositionEl : function(){
28322         return this.wrap;
28323     },
28324
28325     // private
28326     onRender : function(ct, position){
28327         
28328         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28329         //if(this.inputValue !== undefined){
28330         this.wrap = this.el.wrap();
28331         
28332         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28333         
28334         if (this.bodyStyle) {
28335             this.viewEl.applyStyles(this.bodyStyle);
28336         }
28337         //this.viewEl.setStyle('padding', '2px');
28338         
28339         this.setValue(this.value);
28340         
28341     },
28342 /*
28343     // private
28344     initValue : Roo.emptyFn,
28345
28346   */
28347
28348         // private
28349     onClick : function(){
28350         
28351     },
28352
28353     /**
28354      * Sets the checked state of the checkbox.
28355      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28356      */
28357     setValue : function(v){
28358         this.value = v;
28359         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28360         // this might be called before we have a dom element..
28361         if (!this.viewEl) {
28362             return;
28363         }
28364         this.viewEl.dom.innerHTML = html;
28365         Roo.form.DisplayField.superclass.setValue.call(this, v);
28366
28367     }
28368 });/*
28369  * 
28370  * Licence- LGPL
28371  * 
28372  */
28373
28374 /**
28375  * @class Roo.form.DayPicker
28376  * @extends Roo.form.Field
28377  * A Day picker show [M] [T] [W] ....
28378  * @constructor
28379  * Creates a new Day Picker
28380  * @param {Object} config Configuration options
28381  */
28382 Roo.form.DayPicker= function(config){
28383     Roo.form.DayPicker.superclass.constructor.call(this, config);
28384      
28385 };
28386
28387 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28388     /**
28389      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28390      */
28391     focusClass : undefined,
28392     /**
28393      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28394      */
28395     fieldClass: "x-form-field",
28396    
28397     /**
28398      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28399      * {tag: "input", type: "checkbox", autocomplete: "off"})
28400      */
28401     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28402     
28403    
28404     actionMode : 'viewEl', 
28405     //
28406     // private
28407  
28408     inputType : 'hidden',
28409     
28410      
28411     inputElement: false, // real input element?
28412     basedOn: false, // ????
28413     
28414     isFormField: true, // not sure where this is needed!!!!
28415
28416     onResize : function(){
28417         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28418         if(!this.boxLabel){
28419             this.el.alignTo(this.wrap, 'c-c');
28420         }
28421     },
28422
28423     initEvents : function(){
28424         Roo.form.Checkbox.superclass.initEvents.call(this);
28425         this.el.on("click", this.onClick,  this);
28426         this.el.on("change", this.onClick,  this);
28427     },
28428
28429
28430     getResizeEl : function(){
28431         return this.wrap;
28432     },
28433
28434     getPositionEl : function(){
28435         return this.wrap;
28436     },
28437
28438     
28439     // private
28440     onRender : function(ct, position){
28441         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28442        
28443         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28444         
28445         var r1 = '<table><tr>';
28446         var r2 = '<tr class="x-form-daypick-icons">';
28447         for (var i=0; i < 7; i++) {
28448             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28449             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28450         }
28451         
28452         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28453         viewEl.select('img').on('click', this.onClick, this);
28454         this.viewEl = viewEl;   
28455         
28456         
28457         // this will not work on Chrome!!!
28458         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28459         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28460         
28461         
28462           
28463
28464     },
28465
28466     // private
28467     initValue : Roo.emptyFn,
28468
28469     /**
28470      * Returns the checked state of the checkbox.
28471      * @return {Boolean} True if checked, else false
28472      */
28473     getValue : function(){
28474         return this.el.dom.value;
28475         
28476     },
28477
28478         // private
28479     onClick : function(e){ 
28480         //this.setChecked(!this.checked);
28481         Roo.get(e.target).toggleClass('x-menu-item-checked');
28482         this.refreshValue();
28483         //if(this.el.dom.checked != this.checked){
28484         //    this.setValue(this.el.dom.checked);
28485        // }
28486     },
28487     
28488     // private
28489     refreshValue : function()
28490     {
28491         var val = '';
28492         this.viewEl.select('img',true).each(function(e,i,n)  {
28493             val += e.is(".x-menu-item-checked") ? String(n) : '';
28494         });
28495         this.setValue(val, true);
28496     },
28497
28498     /**
28499      * Sets the checked state of the checkbox.
28500      * On is always based on a string comparison between inputValue and the param.
28501      * @param {Boolean/String} value - the value to set 
28502      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28503      */
28504     setValue : function(v,suppressEvent){
28505         if (!this.el.dom) {
28506             return;
28507         }
28508         var old = this.el.dom.value ;
28509         this.el.dom.value = v;
28510         if (suppressEvent) {
28511             return ;
28512         }
28513          
28514         // update display..
28515         this.viewEl.select('img',true).each(function(e,i,n)  {
28516             
28517             var on = e.is(".x-menu-item-checked");
28518             var newv = v.indexOf(String(n)) > -1;
28519             if (on != newv) {
28520                 e.toggleClass('x-menu-item-checked');
28521             }
28522             
28523         });
28524         
28525         
28526         this.fireEvent('change', this, v, old);
28527         
28528         
28529     },
28530    
28531     // handle setting of hidden value by some other method!!?!?
28532     setFromHidden: function()
28533     {
28534         if(!this.el){
28535             return;
28536         }
28537         //console.log("SET FROM HIDDEN");
28538         //alert('setFrom hidden');
28539         this.setValue(this.el.dom.value);
28540     },
28541     
28542     onDestroy : function()
28543     {
28544         if(this.viewEl){
28545             Roo.get(this.viewEl).remove();
28546         }
28547          
28548         Roo.form.DayPicker.superclass.onDestroy.call(this);
28549     }
28550
28551 });//<script type="text/javasscript">
28552  
28553
28554 /**
28555  * @class Roo.DDView
28556  * A DnD enabled version of Roo.View.
28557  * @param {Element/String} container The Element in which to create the View.
28558  * @param {String} tpl The template string used to create the markup for each element of the View
28559  * @param {Object} config The configuration properties. These include all the config options of
28560  * {@link Roo.View} plus some specific to this class.<br>
28561  * <p>
28562  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28563  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28564  * <p>
28565  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28566 .x-view-drag-insert-above {
28567         border-top:1px dotted #3366cc;
28568 }
28569 .x-view-drag-insert-below {
28570         border-bottom:1px dotted #3366cc;
28571 }
28572 </code></pre>
28573  * 
28574  */
28575  
28576 Roo.DDView = function(container, tpl, config) {
28577     Roo.DDView.superclass.constructor.apply(this, arguments);
28578     this.getEl().setStyle("outline", "0px none");
28579     this.getEl().unselectable();
28580     if (this.dragGroup) {
28581                 this.setDraggable(this.dragGroup.split(","));
28582     }
28583     if (this.dropGroup) {
28584                 this.setDroppable(this.dropGroup.split(","));
28585     }
28586     if (this.deletable) {
28587         this.setDeletable();
28588     }
28589     this.isDirtyFlag = false;
28590         this.addEvents({
28591                 "drop" : true
28592         });
28593 };
28594
28595 Roo.extend(Roo.DDView, Roo.View, {
28596 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28597 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28598 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28599 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28600
28601         isFormField: true,
28602
28603         reset: Roo.emptyFn,
28604         
28605         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28606
28607         validate: function() {
28608                 return true;
28609         },
28610         
28611         destroy: function() {
28612                 this.purgeListeners();
28613                 this.getEl.removeAllListeners();
28614                 this.getEl().remove();
28615                 if (this.dragZone) {
28616                         if (this.dragZone.destroy) {
28617                                 this.dragZone.destroy();
28618                         }
28619                 }
28620                 if (this.dropZone) {
28621                         if (this.dropZone.destroy) {
28622                                 this.dropZone.destroy();
28623                         }
28624                 }
28625         },
28626
28627 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28628         getName: function() {
28629                 return this.name;
28630         },
28631
28632 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28633         setValue: function(v) {
28634                 if (!this.store) {
28635                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28636                 }
28637                 var data = {};
28638                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28639                 this.store.proxy = new Roo.data.MemoryProxy(data);
28640                 this.store.load();
28641         },
28642
28643 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28644         getValue: function() {
28645                 var result = '(';
28646                 this.store.each(function(rec) {
28647                         result += rec.id + ',';
28648                 });
28649                 return result.substr(0, result.length - 1) + ')';
28650         },
28651         
28652         getIds: function() {
28653                 var i = 0, result = new Array(this.store.getCount());
28654                 this.store.each(function(rec) {
28655                         result[i++] = rec.id;
28656                 });
28657                 return result;
28658         },
28659         
28660         isDirty: function() {
28661                 return this.isDirtyFlag;
28662         },
28663
28664 /**
28665  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28666  *      whole Element becomes the target, and this causes the drop gesture to append.
28667  */
28668     getTargetFromEvent : function(e) {
28669                 var target = e.getTarget();
28670                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28671                 target = target.parentNode;
28672                 }
28673                 if (!target) {
28674                         target = this.el.dom.lastChild || this.el.dom;
28675                 }
28676                 return target;
28677     },
28678
28679 /**
28680  *      Create the drag data which consists of an object which has the property "ddel" as
28681  *      the drag proxy element. 
28682  */
28683     getDragData : function(e) {
28684         var target = this.findItemFromChild(e.getTarget());
28685                 if(target) {
28686                         this.handleSelection(e);
28687                         var selNodes = this.getSelectedNodes();
28688             var dragData = {
28689                 source: this,
28690                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28691                 nodes: selNodes,
28692                 records: []
28693                         };
28694                         var selectedIndices = this.getSelectedIndexes();
28695                         for (var i = 0; i < selectedIndices.length; i++) {
28696                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28697                         }
28698                         if (selNodes.length == 1) {
28699                                 dragData.ddel = target.cloneNode(true); // the div element
28700                         } else {
28701                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28702                                 div.className = 'multi-proxy';
28703                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28704                                         div.appendChild(selNodes[i].cloneNode(true));
28705                                 }
28706                                 dragData.ddel = div;
28707                         }
28708             //console.log(dragData)
28709             //console.log(dragData.ddel.innerHTML)
28710                         return dragData;
28711                 }
28712         //console.log('nodragData')
28713                 return false;
28714     },
28715     
28716 /**     Specify to which ddGroup items in this DDView may be dragged. */
28717     setDraggable: function(ddGroup) {
28718         if (ddGroup instanceof Array) {
28719                 Roo.each(ddGroup, this.setDraggable, this);
28720                 return;
28721         }
28722         if (this.dragZone) {
28723                 this.dragZone.addToGroup(ddGroup);
28724         } else {
28725                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28726                                 containerScroll: true,
28727                                 ddGroup: ddGroup 
28728
28729                         });
28730 //                      Draggability implies selection. DragZone's mousedown selects the element.
28731                         if (!this.multiSelect) { this.singleSelect = true; }
28732
28733 //                      Wire the DragZone's handlers up to methods in *this*
28734                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28735                 }
28736     },
28737
28738 /**     Specify from which ddGroup this DDView accepts drops. */
28739     setDroppable: function(ddGroup) {
28740         if (ddGroup instanceof Array) {
28741                 Roo.each(ddGroup, this.setDroppable, this);
28742                 return;
28743         }
28744         if (this.dropZone) {
28745                 this.dropZone.addToGroup(ddGroup);
28746         } else {
28747                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28748                                 containerScroll: true,
28749                                 ddGroup: ddGroup
28750                         });
28751
28752 //                      Wire the DropZone's handlers up to methods in *this*
28753                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28754                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28755                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28756                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28757                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28758                 }
28759     },
28760
28761 /**     Decide whether to drop above or below a View node. */
28762     getDropPoint : function(e, n, dd){
28763         if (n == this.el.dom) { return "above"; }
28764                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28765                 var c = t + (b - t) / 2;
28766                 var y = Roo.lib.Event.getPageY(e);
28767                 if(y <= c) {
28768                         return "above";
28769                 }else{
28770                         return "below";
28771                 }
28772     },
28773
28774     onNodeEnter : function(n, dd, e, data){
28775                 return false;
28776     },
28777     
28778     onNodeOver : function(n, dd, e, data){
28779                 var pt = this.getDropPoint(e, n, dd);
28780                 // set the insert point style on the target node
28781                 var dragElClass = this.dropNotAllowed;
28782                 if (pt) {
28783                         var targetElClass;
28784                         if (pt == "above"){
28785                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28786                                 targetElClass = "x-view-drag-insert-above";
28787                         } else {
28788                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28789                                 targetElClass = "x-view-drag-insert-below";
28790                         }
28791                         if (this.lastInsertClass != targetElClass){
28792                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28793                                 this.lastInsertClass = targetElClass;
28794                         }
28795                 }
28796                 return dragElClass;
28797         },
28798
28799     onNodeOut : function(n, dd, e, data){
28800                 this.removeDropIndicators(n);
28801     },
28802
28803     onNodeDrop : function(n, dd, e, data){
28804         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28805                 return false;
28806         }
28807         var pt = this.getDropPoint(e, n, dd);
28808                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28809                 if (pt == "below") { insertAt++; }
28810                 for (var i = 0; i < data.records.length; i++) {
28811                         var r = data.records[i];
28812                         var dup = this.store.getById(r.id);
28813                         if (dup && (dd != this.dragZone)) {
28814                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28815                         } else {
28816                                 if (data.copy) {
28817                                         this.store.insert(insertAt++, r.copy());
28818                                 } else {
28819                                         data.source.isDirtyFlag = true;
28820                                         r.store.remove(r);
28821                                         this.store.insert(insertAt++, r);
28822                                 }
28823                                 this.isDirtyFlag = true;
28824                         }
28825                 }
28826                 this.dragZone.cachedTarget = null;
28827                 return true;
28828     },
28829
28830     removeDropIndicators : function(n){
28831                 if(n){
28832                         Roo.fly(n).removeClass([
28833                                 "x-view-drag-insert-above",
28834                                 "x-view-drag-insert-below"]);
28835                         this.lastInsertClass = "_noclass";
28836                 }
28837     },
28838
28839 /**
28840  *      Utility method. Add a delete option to the DDView's context menu.
28841  *      @param {String} imageUrl The URL of the "delete" icon image.
28842  */
28843         setDeletable: function(imageUrl) {
28844                 if (!this.singleSelect && !this.multiSelect) {
28845                         this.singleSelect = true;
28846                 }
28847                 var c = this.getContextMenu();
28848                 this.contextMenu.on("itemclick", function(item) {
28849                         switch (item.id) {
28850                                 case "delete":
28851                                         this.remove(this.getSelectedIndexes());
28852                                         break;
28853                         }
28854                 }, this);
28855                 this.contextMenu.add({
28856                         icon: imageUrl,
28857                         id: "delete",
28858                         text: 'Delete'
28859                 });
28860         },
28861         
28862 /**     Return the context menu for this DDView. */
28863         getContextMenu: function() {
28864                 if (!this.contextMenu) {
28865 //                      Create the View's context menu
28866                         this.contextMenu = new Roo.menu.Menu({
28867                                 id: this.id + "-contextmenu"
28868                         });
28869                         this.el.on("contextmenu", this.showContextMenu, this);
28870                 }
28871                 return this.contextMenu;
28872         },
28873         
28874         disableContextMenu: function() {
28875                 if (this.contextMenu) {
28876                         this.el.un("contextmenu", this.showContextMenu, this);
28877                 }
28878         },
28879
28880         showContextMenu: function(e, item) {
28881         item = this.findItemFromChild(e.getTarget());
28882                 if (item) {
28883                         e.stopEvent();
28884                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28885                         this.contextMenu.showAt(e.getXY());
28886             }
28887     },
28888
28889 /**
28890  *      Remove {@link Roo.data.Record}s at the specified indices.
28891  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28892  */
28893     remove: function(selectedIndices) {
28894                 selectedIndices = [].concat(selectedIndices);
28895                 for (var i = 0; i < selectedIndices.length; i++) {
28896                         var rec = this.store.getAt(selectedIndices[i]);
28897                         this.store.remove(rec);
28898                 }
28899     },
28900
28901 /**
28902  *      Double click fires the event, but also, if this is draggable, and there is only one other
28903  *      related DropZone, it transfers the selected node.
28904  */
28905     onDblClick : function(e){
28906         var item = this.findItemFromChild(e.getTarget());
28907         if(item){
28908             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28909                 return false;
28910             }
28911             if (this.dragGroup) {
28912                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28913                     while (targets.indexOf(this.dropZone) > -1) {
28914                             targets.remove(this.dropZone);
28915                                 }
28916                     if (targets.length == 1) {
28917                                         this.dragZone.cachedTarget = null;
28918                         var el = Roo.get(targets[0].getEl());
28919                         var box = el.getBox(true);
28920                         targets[0].onNodeDrop(el.dom, {
28921                                 target: el.dom,
28922                                 xy: [box.x, box.y + box.height - 1]
28923                         }, null, this.getDragData(e));
28924                     }
28925                 }
28926         }
28927     },
28928     
28929     handleSelection: function(e) {
28930                 this.dragZone.cachedTarget = null;
28931         var item = this.findItemFromChild(e.getTarget());
28932         if (!item) {
28933                 this.clearSelections(true);
28934                 return;
28935         }
28936                 if (item && (this.multiSelect || this.singleSelect)){
28937                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28938                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28939                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28940                                 this.unselect(item);
28941                         } else {
28942                                 this.select(item, this.multiSelect && e.ctrlKey);
28943                                 this.lastSelection = item;
28944                         }
28945                 }
28946     },
28947
28948     onItemClick : function(item, index, e){
28949                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28950                         return false;
28951                 }
28952                 return true;
28953     },
28954
28955     unselect : function(nodeInfo, suppressEvent){
28956                 var node = this.getNode(nodeInfo);
28957                 if(node && this.isSelected(node)){
28958                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28959                                 Roo.fly(node).removeClass(this.selectedClass);
28960                                 this.selections.remove(node);
28961                                 if(!suppressEvent){
28962                                         this.fireEvent("selectionchange", this, this.selections);
28963                                 }
28964                         }
28965                 }
28966     }
28967 });
28968 /*
28969  * Based on:
28970  * Ext JS Library 1.1.1
28971  * Copyright(c) 2006-2007, Ext JS, LLC.
28972  *
28973  * Originally Released Under LGPL - original licence link has changed is not relivant.
28974  *
28975  * Fork - LGPL
28976  * <script type="text/javascript">
28977  */
28978  
28979 /**
28980  * @class Roo.LayoutManager
28981  * @extends Roo.util.Observable
28982  * Base class for layout managers.
28983  */
28984 Roo.LayoutManager = function(container, config){
28985     Roo.LayoutManager.superclass.constructor.call(this);
28986     this.el = Roo.get(container);
28987     // ie scrollbar fix
28988     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28989         document.body.scroll = "no";
28990     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28991         this.el.position('relative');
28992     }
28993     this.id = this.el.id;
28994     this.el.addClass("x-layout-container");
28995     /** false to disable window resize monitoring @type Boolean */
28996     this.monitorWindowResize = true;
28997     this.regions = {};
28998     this.addEvents({
28999         /**
29000          * @event layout
29001          * Fires when a layout is performed. 
29002          * @param {Roo.LayoutManager} this
29003          */
29004         "layout" : true,
29005         /**
29006          * @event regionresized
29007          * Fires when the user resizes a region. 
29008          * @param {Roo.LayoutRegion} region The resized region
29009          * @param {Number} newSize The new size (width for east/west, height for north/south)
29010          */
29011         "regionresized" : true,
29012         /**
29013          * @event regioncollapsed
29014          * Fires when a region is collapsed. 
29015          * @param {Roo.LayoutRegion} region The collapsed region
29016          */
29017         "regioncollapsed" : true,
29018         /**
29019          * @event regionexpanded
29020          * Fires when a region is expanded.  
29021          * @param {Roo.LayoutRegion} region The expanded region
29022          */
29023         "regionexpanded" : true
29024     });
29025     this.updating = false;
29026     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29027 };
29028
29029 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29030     /**
29031      * Returns true if this layout is currently being updated
29032      * @return {Boolean}
29033      */
29034     isUpdating : function(){
29035         return this.updating; 
29036     },
29037     
29038     /**
29039      * Suspend the LayoutManager from doing auto-layouts while
29040      * making multiple add or remove calls
29041      */
29042     beginUpdate : function(){
29043         this.updating = true;    
29044     },
29045     
29046     /**
29047      * Restore auto-layouts and optionally disable the manager from performing a layout
29048      * @param {Boolean} noLayout true to disable a layout update 
29049      */
29050     endUpdate : function(noLayout){
29051         this.updating = false;
29052         if(!noLayout){
29053             this.layout();
29054         }    
29055     },
29056     
29057     layout: function(){
29058         
29059     },
29060     
29061     onRegionResized : function(region, newSize){
29062         this.fireEvent("regionresized", region, newSize);
29063         this.layout();
29064     },
29065     
29066     onRegionCollapsed : function(region){
29067         this.fireEvent("regioncollapsed", region);
29068     },
29069     
29070     onRegionExpanded : function(region){
29071         this.fireEvent("regionexpanded", region);
29072     },
29073         
29074     /**
29075      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29076      * performs box-model adjustments.
29077      * @return {Object} The size as an object {width: (the width), height: (the height)}
29078      */
29079     getViewSize : function(){
29080         var size;
29081         if(this.el.dom != document.body){
29082             size = this.el.getSize();
29083         }else{
29084             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29085         }
29086         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29087         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29088         return size;
29089     },
29090     
29091     /**
29092      * Returns the Element this layout is bound to.
29093      * @return {Roo.Element}
29094      */
29095     getEl : function(){
29096         return this.el;
29097     },
29098     
29099     /**
29100      * Returns the specified region.
29101      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29102      * @return {Roo.LayoutRegion}
29103      */
29104     getRegion : function(target){
29105         return this.regions[target.toLowerCase()];
29106     },
29107     
29108     onWindowResize : function(){
29109         if(this.monitorWindowResize){
29110             this.layout();
29111         }
29112     }
29113 });/*
29114  * Based on:
29115  * Ext JS Library 1.1.1
29116  * Copyright(c) 2006-2007, Ext JS, LLC.
29117  *
29118  * Originally Released Under LGPL - original licence link has changed is not relivant.
29119  *
29120  * Fork - LGPL
29121  * <script type="text/javascript">
29122  */
29123 /**
29124  * @class Roo.BorderLayout
29125  * @extends Roo.LayoutManager
29126  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29127  * please see: <br><br>
29128  * <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>
29129  * <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>
29130  * Example:
29131  <pre><code>
29132  var layout = new Roo.BorderLayout(document.body, {
29133     north: {
29134         initialSize: 25,
29135         titlebar: false
29136     },
29137     west: {
29138         split:true,
29139         initialSize: 200,
29140         minSize: 175,
29141         maxSize: 400,
29142         titlebar: true,
29143         collapsible: true
29144     },
29145     east: {
29146         split:true,
29147         initialSize: 202,
29148         minSize: 175,
29149         maxSize: 400,
29150         titlebar: true,
29151         collapsible: true
29152     },
29153     south: {
29154         split:true,
29155         initialSize: 100,
29156         minSize: 100,
29157         maxSize: 200,
29158         titlebar: true,
29159         collapsible: true
29160     },
29161     center: {
29162         titlebar: true,
29163         autoScroll:true,
29164         resizeTabs: true,
29165         minTabWidth: 50,
29166         preferredTabWidth: 150
29167     }
29168 });
29169
29170 // shorthand
29171 var CP = Roo.ContentPanel;
29172
29173 layout.beginUpdate();
29174 layout.add("north", new CP("north", "North"));
29175 layout.add("south", new CP("south", {title: "South", closable: true}));
29176 layout.add("west", new CP("west", {title: "West"}));
29177 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29178 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29179 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29180 layout.getRegion("center").showPanel("center1");
29181 layout.endUpdate();
29182 </code></pre>
29183
29184 <b>The container the layout is rendered into can be either the body element or any other element.
29185 If it is not the body element, the container needs to either be an absolute positioned element,
29186 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29187 the container size if it is not the body element.</b>
29188
29189 * @constructor
29190 * Create a new BorderLayout
29191 * @param {String/HTMLElement/Element} container The container this layout is bound to
29192 * @param {Object} config Configuration options
29193  */
29194 Roo.BorderLayout = function(container, config){
29195     config = config || {};
29196     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29197     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29198     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29199         var target = this.factory.validRegions[i];
29200         if(config[target]){
29201             this.addRegion(target, config[target]);
29202         }
29203     }
29204 };
29205
29206 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29207     /**
29208      * Creates and adds a new region if it doesn't already exist.
29209      * @param {String} target The target region key (north, south, east, west or center).
29210      * @param {Object} config The regions config object
29211      * @return {BorderLayoutRegion} The new region
29212      */
29213     addRegion : function(target, config){
29214         if(!this.regions[target]){
29215             var r = this.factory.create(target, this, config);
29216             this.bindRegion(target, r);
29217         }
29218         return this.regions[target];
29219     },
29220
29221     // private (kinda)
29222     bindRegion : function(name, r){
29223         this.regions[name] = r;
29224         r.on("visibilitychange", this.layout, this);
29225         r.on("paneladded", this.layout, this);
29226         r.on("panelremoved", this.layout, this);
29227         r.on("invalidated", this.layout, this);
29228         r.on("resized", this.onRegionResized, this);
29229         r.on("collapsed", this.onRegionCollapsed, this);
29230         r.on("expanded", this.onRegionExpanded, this);
29231     },
29232
29233     /**
29234      * Performs a layout update.
29235      */
29236     layout : function(){
29237         if(this.updating) return;
29238         var size = this.getViewSize();
29239         var w = size.width;
29240         var h = size.height;
29241         var centerW = w;
29242         var centerH = h;
29243         var centerY = 0;
29244         var centerX = 0;
29245         //var x = 0, y = 0;
29246
29247         var rs = this.regions;
29248         var north = rs["north"];
29249         var south = rs["south"]; 
29250         var west = rs["west"];
29251         var east = rs["east"];
29252         var center = rs["center"];
29253         //if(this.hideOnLayout){ // not supported anymore
29254             //c.el.setStyle("display", "none");
29255         //}
29256         if(north && north.isVisible()){
29257             var b = north.getBox();
29258             var m = north.getMargins();
29259             b.width = w - (m.left+m.right);
29260             b.x = m.left;
29261             b.y = m.top;
29262             centerY = b.height + b.y + m.bottom;
29263             centerH -= centerY;
29264             north.updateBox(this.safeBox(b));
29265         }
29266         if(south && south.isVisible()){
29267             var b = south.getBox();
29268             var m = south.getMargins();
29269             b.width = w - (m.left+m.right);
29270             b.x = m.left;
29271             var totalHeight = (b.height + m.top + m.bottom);
29272             b.y = h - totalHeight + m.top;
29273             centerH -= totalHeight;
29274             south.updateBox(this.safeBox(b));
29275         }
29276         if(west && west.isVisible()){
29277             var b = west.getBox();
29278             var m = west.getMargins();
29279             b.height = centerH - (m.top+m.bottom);
29280             b.x = m.left;
29281             b.y = centerY + m.top;
29282             var totalWidth = (b.width + m.left + m.right);
29283             centerX += totalWidth;
29284             centerW -= totalWidth;
29285             west.updateBox(this.safeBox(b));
29286         }
29287         if(east && east.isVisible()){
29288             var b = east.getBox();
29289             var m = east.getMargins();
29290             b.height = centerH - (m.top+m.bottom);
29291             var totalWidth = (b.width + m.left + m.right);
29292             b.x = w - totalWidth + m.left;
29293             b.y = centerY + m.top;
29294             centerW -= totalWidth;
29295             east.updateBox(this.safeBox(b));
29296         }
29297         if(center){
29298             var m = center.getMargins();
29299             var centerBox = {
29300                 x: centerX + m.left,
29301                 y: centerY + m.top,
29302                 width: centerW - (m.left+m.right),
29303                 height: centerH - (m.top+m.bottom)
29304             };
29305             //if(this.hideOnLayout){
29306                 //center.el.setStyle("display", "block");
29307             //}
29308             center.updateBox(this.safeBox(centerBox));
29309         }
29310         this.el.repaint();
29311         this.fireEvent("layout", this);
29312     },
29313
29314     // private
29315     safeBox : function(box){
29316         box.width = Math.max(0, box.width);
29317         box.height = Math.max(0, box.height);
29318         return box;
29319     },
29320
29321     /**
29322      * Adds a ContentPanel (or subclass) to this layout.
29323      * @param {String} target The target region key (north, south, east, west or center).
29324      * @param {Roo.ContentPanel} panel The panel to add
29325      * @return {Roo.ContentPanel} The added panel
29326      */
29327     add : function(target, panel){
29328          
29329         target = target.toLowerCase();
29330         return this.regions[target].add(panel);
29331     },
29332
29333     /**
29334      * Remove a ContentPanel (or subclass) to this layout.
29335      * @param {String} target The target region key (north, south, east, west or center).
29336      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29337      * @return {Roo.ContentPanel} The removed panel
29338      */
29339     remove : function(target, panel){
29340         target = target.toLowerCase();
29341         return this.regions[target].remove(panel);
29342     },
29343
29344     /**
29345      * Searches all regions for a panel with the specified id
29346      * @param {String} panelId
29347      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29348      */
29349     findPanel : function(panelId){
29350         var rs = this.regions;
29351         for(var target in rs){
29352             if(typeof rs[target] != "function"){
29353                 var p = rs[target].getPanel(panelId);
29354                 if(p){
29355                     return p;
29356                 }
29357             }
29358         }
29359         return null;
29360     },
29361
29362     /**
29363      * Searches all regions for a panel with the specified id and activates (shows) it.
29364      * @param {String/ContentPanel} panelId The panels id or the panel itself
29365      * @return {Roo.ContentPanel} The shown panel or null
29366      */
29367     showPanel : function(panelId) {
29368       var rs = this.regions;
29369       for(var target in rs){
29370          var r = rs[target];
29371          if(typeof r != "function"){
29372             if(r.hasPanel(panelId)){
29373                return r.showPanel(panelId);
29374             }
29375          }
29376       }
29377       return null;
29378    },
29379
29380    /**
29381      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29382      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29383      */
29384     restoreState : function(provider){
29385         if(!provider){
29386             provider = Roo.state.Manager;
29387         }
29388         var sm = new Roo.LayoutStateManager();
29389         sm.init(this, provider);
29390     },
29391
29392     /**
29393      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29394      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29395      * a valid ContentPanel config object.  Example:
29396      * <pre><code>
29397 // Create the main layout
29398 var layout = new Roo.BorderLayout('main-ct', {
29399     west: {
29400         split:true,
29401         minSize: 175,
29402         titlebar: true
29403     },
29404     center: {
29405         title:'Components'
29406     }
29407 }, 'main-ct');
29408
29409 // Create and add multiple ContentPanels at once via configs
29410 layout.batchAdd({
29411    west: {
29412        id: 'source-files',
29413        autoCreate:true,
29414        title:'Ext Source Files',
29415        autoScroll:true,
29416        fitToFrame:true
29417    },
29418    center : {
29419        el: cview,
29420        autoScroll:true,
29421        fitToFrame:true,
29422        toolbar: tb,
29423        resizeEl:'cbody'
29424    }
29425 });
29426 </code></pre>
29427      * @param {Object} regions An object containing ContentPanel configs by region name
29428      */
29429     batchAdd : function(regions){
29430         this.beginUpdate();
29431         for(var rname in regions){
29432             var lr = this.regions[rname];
29433             if(lr){
29434                 this.addTypedPanels(lr, regions[rname]);
29435             }
29436         }
29437         this.endUpdate();
29438     },
29439
29440     // private
29441     addTypedPanels : function(lr, ps){
29442         if(typeof ps == 'string'){
29443             lr.add(new Roo.ContentPanel(ps));
29444         }
29445         else if(ps instanceof Array){
29446             for(var i =0, len = ps.length; i < len; i++){
29447                 this.addTypedPanels(lr, ps[i]);
29448             }
29449         }
29450         else if(!ps.events){ // raw config?
29451             var el = ps.el;
29452             delete ps.el; // prevent conflict
29453             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29454         }
29455         else {  // panel object assumed!
29456             lr.add(ps);
29457         }
29458     },
29459     /**
29460      * Adds a xtype elements to the layout.
29461      * <pre><code>
29462
29463 layout.addxtype({
29464        xtype : 'ContentPanel',
29465        region: 'west',
29466        items: [ .... ]
29467    }
29468 );
29469
29470 layout.addxtype({
29471         xtype : 'NestedLayoutPanel',
29472         region: 'west',
29473         layout: {
29474            center: { },
29475            west: { }   
29476         },
29477         items : [ ... list of content panels or nested layout panels.. ]
29478    }
29479 );
29480 </code></pre>
29481      * @param {Object} cfg Xtype definition of item to add.
29482      */
29483     addxtype : function(cfg)
29484     {
29485         // basically accepts a pannel...
29486         // can accept a layout region..!?!?
29487        // console.log('BorderLayout add ' + cfg.xtype)
29488         
29489         if (!cfg.xtype.match(/Panel$/)) {
29490             return false;
29491         }
29492         var ret = false;
29493         var region = cfg.region;
29494         delete cfg.region;
29495         
29496           
29497         var xitems = [];
29498         if (cfg.items) {
29499             xitems = cfg.items;
29500             delete cfg.items;
29501         }
29502         
29503         
29504         switch(cfg.xtype) 
29505         {
29506             case 'ContentPanel':  // ContentPanel (el, cfg)
29507             case 'ScrollPanel':  // ContentPanel (el, cfg)
29508                 if(cfg.autoCreate) {
29509                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29510                 } else {
29511                     var el = this.el.createChild();
29512                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29513                 }
29514                 
29515                 this.add(region, ret);
29516                 break;
29517             
29518             
29519             case 'TreePanel': // our new panel!
29520                 cfg.el = this.el.createChild();
29521                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29522                 this.add(region, ret);
29523                 break;
29524             
29525             case 'NestedLayoutPanel': 
29526                 // create a new Layout (which is  a Border Layout...
29527                 var el = this.el.createChild();
29528                 var clayout = cfg.layout;
29529                 delete cfg.layout;
29530                 clayout.items   = clayout.items  || [];
29531                 // replace this exitems with the clayout ones..
29532                 xitems = clayout.items;
29533                  
29534                 
29535                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29536                     cfg.background = false;
29537                 }
29538                 var layout = new Roo.BorderLayout(el, clayout);
29539                 
29540                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29541                 //console.log('adding nested layout panel '  + cfg.toSource());
29542                 this.add(region, ret);
29543                 
29544                 break;
29545                 
29546             case 'GridPanel': 
29547             
29548                 // needs grid and region
29549                 
29550                 //var el = this.getRegion(region).el.createChild();
29551                 var el = this.el.createChild();
29552                 // create the grid first...
29553                 
29554                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29555                 delete cfg.grid;
29556                 if (region == 'center' && this.active ) {
29557                     cfg.background = false;
29558                 }
29559                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29560                 
29561                 this.add(region, ret);
29562                 if (cfg.background) {
29563                     ret.on('activate', function(gp) {
29564                         if (!gp.grid.rendered) {
29565                             gp.grid.render();
29566                         }
29567                     });
29568                 } else {
29569                     grid.render();
29570                 }
29571                 break;
29572            
29573                
29574                 
29575                 
29576             default: 
29577                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29578                 return null;
29579              // GridPanel (grid, cfg)
29580             
29581         }
29582         this.beginUpdate();
29583         // add children..
29584         Roo.each(xitems, function(i)  {
29585             ret.addxtype(i);
29586         });
29587         this.endUpdate();
29588         return ret;
29589         
29590     }
29591 });
29592
29593 /**
29594  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29595  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29596  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29597  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29598  * <pre><code>
29599 // shorthand
29600 var CP = Roo.ContentPanel;
29601
29602 var layout = Roo.BorderLayout.create({
29603     north: {
29604         initialSize: 25,
29605         titlebar: false,
29606         panels: [new CP("north", "North")]
29607     },
29608     west: {
29609         split:true,
29610         initialSize: 200,
29611         minSize: 175,
29612         maxSize: 400,
29613         titlebar: true,
29614         collapsible: true,
29615         panels: [new CP("west", {title: "West"})]
29616     },
29617     east: {
29618         split:true,
29619         initialSize: 202,
29620         minSize: 175,
29621         maxSize: 400,
29622         titlebar: true,
29623         collapsible: true,
29624         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29625     },
29626     south: {
29627         split:true,
29628         initialSize: 100,
29629         minSize: 100,
29630         maxSize: 200,
29631         titlebar: true,
29632         collapsible: true,
29633         panels: [new CP("south", {title: "South", closable: true})]
29634     },
29635     center: {
29636         titlebar: true,
29637         autoScroll:true,
29638         resizeTabs: true,
29639         minTabWidth: 50,
29640         preferredTabWidth: 150,
29641         panels: [
29642             new CP("center1", {title: "Close Me", closable: true}),
29643             new CP("center2", {title: "Center Panel", closable: false})
29644         ]
29645     }
29646 }, document.body);
29647
29648 layout.getRegion("center").showPanel("center1");
29649 </code></pre>
29650  * @param config
29651  * @param targetEl
29652  */
29653 Roo.BorderLayout.create = function(config, targetEl){
29654     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29655     layout.beginUpdate();
29656     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29657     for(var j = 0, jlen = regions.length; j < jlen; j++){
29658         var lr = regions[j];
29659         if(layout.regions[lr] && config[lr].panels){
29660             var r = layout.regions[lr];
29661             var ps = config[lr].panels;
29662             layout.addTypedPanels(r, ps);
29663         }
29664     }
29665     layout.endUpdate();
29666     return layout;
29667 };
29668
29669 // private
29670 Roo.BorderLayout.RegionFactory = {
29671     // private
29672     validRegions : ["north","south","east","west","center"],
29673
29674     // private
29675     create : function(target, mgr, config){
29676         target = target.toLowerCase();
29677         if(config.lightweight || config.basic){
29678             return new Roo.BasicLayoutRegion(mgr, config, target);
29679         }
29680         switch(target){
29681             case "north":
29682                 return new Roo.NorthLayoutRegion(mgr, config);
29683             case "south":
29684                 return new Roo.SouthLayoutRegion(mgr, config);
29685             case "east":
29686                 return new Roo.EastLayoutRegion(mgr, config);
29687             case "west":
29688                 return new Roo.WestLayoutRegion(mgr, config);
29689             case "center":
29690                 return new Roo.CenterLayoutRegion(mgr, config);
29691         }
29692         throw 'Layout region "'+target+'" not supported.';
29693     }
29694 };/*
29695  * Based on:
29696  * Ext JS Library 1.1.1
29697  * Copyright(c) 2006-2007, Ext JS, LLC.
29698  *
29699  * Originally Released Under LGPL - original licence link has changed is not relivant.
29700  *
29701  * Fork - LGPL
29702  * <script type="text/javascript">
29703  */
29704  
29705 /**
29706  * @class Roo.BasicLayoutRegion
29707  * @extends Roo.util.Observable
29708  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29709  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29710  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29711  */
29712 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29713     this.mgr = mgr;
29714     this.position  = pos;
29715     this.events = {
29716         /**
29717          * @scope Roo.BasicLayoutRegion
29718          */
29719         
29720         /**
29721          * @event beforeremove
29722          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29723          * @param {Roo.LayoutRegion} this
29724          * @param {Roo.ContentPanel} panel The panel
29725          * @param {Object} e The cancel event object
29726          */
29727         "beforeremove" : true,
29728         /**
29729          * @event invalidated
29730          * Fires when the layout for this region is changed.
29731          * @param {Roo.LayoutRegion} this
29732          */
29733         "invalidated" : true,
29734         /**
29735          * @event visibilitychange
29736          * Fires when this region is shown or hidden 
29737          * @param {Roo.LayoutRegion} this
29738          * @param {Boolean} visibility true or false
29739          */
29740         "visibilitychange" : true,
29741         /**
29742          * @event paneladded
29743          * Fires when a panel is added. 
29744          * @param {Roo.LayoutRegion} this
29745          * @param {Roo.ContentPanel} panel The panel
29746          */
29747         "paneladded" : true,
29748         /**
29749          * @event panelremoved
29750          * Fires when a panel is removed. 
29751          * @param {Roo.LayoutRegion} this
29752          * @param {Roo.ContentPanel} panel The panel
29753          */
29754         "panelremoved" : true,
29755         /**
29756          * @event collapsed
29757          * Fires when this region is collapsed.
29758          * @param {Roo.LayoutRegion} this
29759          */
29760         "collapsed" : true,
29761         /**
29762          * @event expanded
29763          * Fires when this region is expanded.
29764          * @param {Roo.LayoutRegion} this
29765          */
29766         "expanded" : true,
29767         /**
29768          * @event slideshow
29769          * Fires when this region is slid into view.
29770          * @param {Roo.LayoutRegion} this
29771          */
29772         "slideshow" : true,
29773         /**
29774          * @event slidehide
29775          * Fires when this region slides out of view. 
29776          * @param {Roo.LayoutRegion} this
29777          */
29778         "slidehide" : true,
29779         /**
29780          * @event panelactivated
29781          * Fires when a panel is activated. 
29782          * @param {Roo.LayoutRegion} this
29783          * @param {Roo.ContentPanel} panel The activated panel
29784          */
29785         "panelactivated" : true,
29786         /**
29787          * @event resized
29788          * Fires when the user resizes this region. 
29789          * @param {Roo.LayoutRegion} this
29790          * @param {Number} newSize The new size (width for east/west, height for north/south)
29791          */
29792         "resized" : true
29793     };
29794     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29795     this.panels = new Roo.util.MixedCollection();
29796     this.panels.getKey = this.getPanelId.createDelegate(this);
29797     this.box = null;
29798     this.activePanel = null;
29799     // ensure listeners are added...
29800     
29801     if (config.listeners || config.events) {
29802         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29803             listeners : config.listeners || {},
29804             events : config.events || {}
29805         });
29806     }
29807     
29808     if(skipConfig !== true){
29809         this.applyConfig(config);
29810     }
29811 };
29812
29813 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29814     getPanelId : function(p){
29815         return p.getId();
29816     },
29817     
29818     applyConfig : function(config){
29819         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29820         this.config = config;
29821         
29822     },
29823     
29824     /**
29825      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29826      * the width, for horizontal (north, south) the height.
29827      * @param {Number} newSize The new width or height
29828      */
29829     resizeTo : function(newSize){
29830         var el = this.el ? this.el :
29831                  (this.activePanel ? this.activePanel.getEl() : null);
29832         if(el){
29833             switch(this.position){
29834                 case "east":
29835                 case "west":
29836                     el.setWidth(newSize);
29837                     this.fireEvent("resized", this, newSize);
29838                 break;
29839                 case "north":
29840                 case "south":
29841                     el.setHeight(newSize);
29842                     this.fireEvent("resized", this, newSize);
29843                 break;                
29844             }
29845         }
29846     },
29847     
29848     getBox : function(){
29849         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29850     },
29851     
29852     getMargins : function(){
29853         return this.margins;
29854     },
29855     
29856     updateBox : function(box){
29857         this.box = box;
29858         var el = this.activePanel.getEl();
29859         el.dom.style.left = box.x + "px";
29860         el.dom.style.top = box.y + "px";
29861         this.activePanel.setSize(box.width, box.height);
29862     },
29863     
29864     /**
29865      * Returns the container element for this region.
29866      * @return {Roo.Element}
29867      */
29868     getEl : function(){
29869         return this.activePanel;
29870     },
29871     
29872     /**
29873      * Returns true if this region is currently visible.
29874      * @return {Boolean}
29875      */
29876     isVisible : function(){
29877         return this.activePanel ? true : false;
29878     },
29879     
29880     setActivePanel : function(panel){
29881         panel = this.getPanel(panel);
29882         if(this.activePanel && this.activePanel != panel){
29883             this.activePanel.setActiveState(false);
29884             this.activePanel.getEl().setLeftTop(-10000,-10000);
29885         }
29886         this.activePanel = panel;
29887         panel.setActiveState(true);
29888         if(this.box){
29889             panel.setSize(this.box.width, this.box.height);
29890         }
29891         this.fireEvent("panelactivated", this, panel);
29892         this.fireEvent("invalidated");
29893     },
29894     
29895     /**
29896      * Show the specified panel.
29897      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29898      * @return {Roo.ContentPanel} The shown panel or null
29899      */
29900     showPanel : function(panel){
29901         if(panel = this.getPanel(panel)){
29902             this.setActivePanel(panel);
29903         }
29904         return panel;
29905     },
29906     
29907     /**
29908      * Get the active panel for this region.
29909      * @return {Roo.ContentPanel} The active panel or null
29910      */
29911     getActivePanel : function(){
29912         return this.activePanel;
29913     },
29914     
29915     /**
29916      * Add the passed ContentPanel(s)
29917      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29918      * @return {Roo.ContentPanel} The panel added (if only one was added)
29919      */
29920     add : function(panel){
29921         if(arguments.length > 1){
29922             for(var i = 0, len = arguments.length; i < len; i++) {
29923                 this.add(arguments[i]);
29924             }
29925             return null;
29926         }
29927         if(this.hasPanel(panel)){
29928             this.showPanel(panel);
29929             return panel;
29930         }
29931         var el = panel.getEl();
29932         if(el.dom.parentNode != this.mgr.el.dom){
29933             this.mgr.el.dom.appendChild(el.dom);
29934         }
29935         if(panel.setRegion){
29936             panel.setRegion(this);
29937         }
29938         this.panels.add(panel);
29939         el.setStyle("position", "absolute");
29940         if(!panel.background){
29941             this.setActivePanel(panel);
29942             if(this.config.initialSize && this.panels.getCount()==1){
29943                 this.resizeTo(this.config.initialSize);
29944             }
29945         }
29946         this.fireEvent("paneladded", this, panel);
29947         return panel;
29948     },
29949     
29950     /**
29951      * Returns true if the panel is in this region.
29952      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29953      * @return {Boolean}
29954      */
29955     hasPanel : function(panel){
29956         if(typeof panel == "object"){ // must be panel obj
29957             panel = panel.getId();
29958         }
29959         return this.getPanel(panel) ? true : false;
29960     },
29961     
29962     /**
29963      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29964      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29965      * @param {Boolean} preservePanel Overrides the config preservePanel option
29966      * @return {Roo.ContentPanel} The panel that was removed
29967      */
29968     remove : function(panel, preservePanel){
29969         panel = this.getPanel(panel);
29970         if(!panel){
29971             return null;
29972         }
29973         var e = {};
29974         this.fireEvent("beforeremove", this, panel, e);
29975         if(e.cancel === true){
29976             return null;
29977         }
29978         var panelId = panel.getId();
29979         this.panels.removeKey(panelId);
29980         return panel;
29981     },
29982     
29983     /**
29984      * Returns the panel specified or null if it's not in this region.
29985      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29986      * @return {Roo.ContentPanel}
29987      */
29988     getPanel : function(id){
29989         if(typeof id == "object"){ // must be panel obj
29990             return id;
29991         }
29992         return this.panels.get(id);
29993     },
29994     
29995     /**
29996      * Returns this regions position (north/south/east/west/center).
29997      * @return {String} 
29998      */
29999     getPosition: function(){
30000         return this.position;    
30001     }
30002 });/*
30003  * Based on:
30004  * Ext JS Library 1.1.1
30005  * Copyright(c) 2006-2007, Ext JS, LLC.
30006  *
30007  * Originally Released Under LGPL - original licence link has changed is not relivant.
30008  *
30009  * Fork - LGPL
30010  * <script type="text/javascript">
30011  */
30012  
30013 /**
30014  * @class Roo.LayoutRegion
30015  * @extends Roo.BasicLayoutRegion
30016  * This class represents a region in a layout manager.
30017  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30018  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30019  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30020  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30021  * @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})
30022  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30023  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30024  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30025  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30026  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30027  * @cfg {String}    title           The title for the region (overrides panel titles)
30028  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30029  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30030  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30031  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30032  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30033  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30034  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30035  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30036  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30037  * @cfg {Boolean}   showPin         True to show a pin button
30038  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30039  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30040  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30041  * @cfg {Number}    width           For East/West panels
30042  * @cfg {Number}    height          For North/South panels
30043  * @cfg {Boolean}   split           To show the splitter
30044  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30045  */
30046 Roo.LayoutRegion = function(mgr, config, pos){
30047     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30048     var dh = Roo.DomHelper;
30049     /** This region's container element 
30050     * @type Roo.Element */
30051     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30052     /** This region's title element 
30053     * @type Roo.Element */
30054
30055     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30056         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30057         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30058     ]}, true);
30059     this.titleEl.enableDisplayMode();
30060     /** This region's title text element 
30061     * @type HTMLElement */
30062     this.titleTextEl = this.titleEl.dom.firstChild;
30063     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30064     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30065     this.closeBtn.enableDisplayMode();
30066     this.closeBtn.on("click", this.closeClicked, this);
30067     this.closeBtn.hide();
30068
30069     this.createBody(config);
30070     this.visible = true;
30071     this.collapsed = false;
30072
30073     if(config.hideWhenEmpty){
30074         this.hide();
30075         this.on("paneladded", this.validateVisibility, this);
30076         this.on("panelremoved", this.validateVisibility, this);
30077     }
30078     this.applyConfig(config);
30079 };
30080
30081 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30082
30083     createBody : function(){
30084         /** This region's body element 
30085         * @type Roo.Element */
30086         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30087     },
30088
30089     applyConfig : function(c){
30090         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30091             var dh = Roo.DomHelper;
30092             if(c.titlebar !== false){
30093                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30094                 this.collapseBtn.on("click", this.collapse, this);
30095                 this.collapseBtn.enableDisplayMode();
30096
30097                 if(c.showPin === true || this.showPin){
30098                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30099                     this.stickBtn.enableDisplayMode();
30100                     this.stickBtn.on("click", this.expand, this);
30101                     this.stickBtn.hide();
30102                 }
30103             }
30104             /** This region's collapsed element
30105             * @type Roo.Element */
30106             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30107                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30108             ]}, true);
30109             if(c.floatable !== false){
30110                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30111                this.collapsedEl.on("click", this.collapseClick, this);
30112             }
30113
30114             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30115                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30116                    id: "message", unselectable: "on", style:{"float":"left"}});
30117                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30118              }
30119             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30120             this.expandBtn.on("click", this.expand, this);
30121         }
30122         if(this.collapseBtn){
30123             this.collapseBtn.setVisible(c.collapsible == true);
30124         }
30125         this.cmargins = c.cmargins || this.cmargins ||
30126                          (this.position == "west" || this.position == "east" ?
30127                              {top: 0, left: 2, right:2, bottom: 0} :
30128                              {top: 2, left: 0, right:0, bottom: 2});
30129         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30130         this.bottomTabs = c.tabPosition != "top";
30131         this.autoScroll = c.autoScroll || false;
30132         if(this.autoScroll){
30133             this.bodyEl.setStyle("overflow", "auto");
30134         }else{
30135             this.bodyEl.setStyle("overflow", "hidden");
30136         }
30137         //if(c.titlebar !== false){
30138             if((!c.titlebar && !c.title) || c.titlebar === false){
30139                 this.titleEl.hide();
30140             }else{
30141                 this.titleEl.show();
30142                 if(c.title){
30143                     this.titleTextEl.innerHTML = c.title;
30144                 }
30145             }
30146         //}
30147         this.duration = c.duration || .30;
30148         this.slideDuration = c.slideDuration || .45;
30149         this.config = c;
30150         if(c.collapsed){
30151             this.collapse(true);
30152         }
30153         if(c.hidden){
30154             this.hide();
30155         }
30156     },
30157     /**
30158      * Returns true if this region is currently visible.
30159      * @return {Boolean}
30160      */
30161     isVisible : function(){
30162         return this.visible;
30163     },
30164
30165     /**
30166      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30167      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30168      */
30169     setCollapsedTitle : function(title){
30170         title = title || "&#160;";
30171         if(this.collapsedTitleTextEl){
30172             this.collapsedTitleTextEl.innerHTML = title;
30173         }
30174     },
30175
30176     getBox : function(){
30177         var b;
30178         if(!this.collapsed){
30179             b = this.el.getBox(false, true);
30180         }else{
30181             b = this.collapsedEl.getBox(false, true);
30182         }
30183         return b;
30184     },
30185
30186     getMargins : function(){
30187         return this.collapsed ? this.cmargins : this.margins;
30188     },
30189
30190     highlight : function(){
30191         this.el.addClass("x-layout-panel-dragover");
30192     },
30193
30194     unhighlight : function(){
30195         this.el.removeClass("x-layout-panel-dragover");
30196     },
30197
30198     updateBox : function(box){
30199         this.box = box;
30200         if(!this.collapsed){
30201             this.el.dom.style.left = box.x + "px";
30202             this.el.dom.style.top = box.y + "px";
30203             this.updateBody(box.width, box.height);
30204         }else{
30205             this.collapsedEl.dom.style.left = box.x + "px";
30206             this.collapsedEl.dom.style.top = box.y + "px";
30207             this.collapsedEl.setSize(box.width, box.height);
30208         }
30209         if(this.tabs){
30210             this.tabs.autoSizeTabs();
30211         }
30212     },
30213
30214     updateBody : function(w, h){
30215         if(w !== null){
30216             this.el.setWidth(w);
30217             w -= this.el.getBorderWidth("rl");
30218             if(this.config.adjustments){
30219                 w += this.config.adjustments[0];
30220             }
30221         }
30222         if(h !== null){
30223             this.el.setHeight(h);
30224             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30225             h -= this.el.getBorderWidth("tb");
30226             if(this.config.adjustments){
30227                 h += this.config.adjustments[1];
30228             }
30229             this.bodyEl.setHeight(h);
30230             if(this.tabs){
30231                 h = this.tabs.syncHeight(h);
30232             }
30233         }
30234         if(this.panelSize){
30235             w = w !== null ? w : this.panelSize.width;
30236             h = h !== null ? h : this.panelSize.height;
30237         }
30238         if(this.activePanel){
30239             var el = this.activePanel.getEl();
30240             w = w !== null ? w : el.getWidth();
30241             h = h !== null ? h : el.getHeight();
30242             this.panelSize = {width: w, height: h};
30243             this.activePanel.setSize(w, h);
30244         }
30245         if(Roo.isIE && this.tabs){
30246             this.tabs.el.repaint();
30247         }
30248     },
30249
30250     /**
30251      * Returns the container element for this region.
30252      * @return {Roo.Element}
30253      */
30254     getEl : function(){
30255         return this.el;
30256     },
30257
30258     /**
30259      * Hides this region.
30260      */
30261     hide : function(){
30262         if(!this.collapsed){
30263             this.el.dom.style.left = "-2000px";
30264             this.el.hide();
30265         }else{
30266             this.collapsedEl.dom.style.left = "-2000px";
30267             this.collapsedEl.hide();
30268         }
30269         this.visible = false;
30270         this.fireEvent("visibilitychange", this, false);
30271     },
30272
30273     /**
30274      * Shows this region if it was previously hidden.
30275      */
30276     show : function(){
30277         if(!this.collapsed){
30278             this.el.show();
30279         }else{
30280             this.collapsedEl.show();
30281         }
30282         this.visible = true;
30283         this.fireEvent("visibilitychange", this, true);
30284     },
30285
30286     closeClicked : function(){
30287         if(this.activePanel){
30288             this.remove(this.activePanel);
30289         }
30290     },
30291
30292     collapseClick : function(e){
30293         if(this.isSlid){
30294            e.stopPropagation();
30295            this.slideIn();
30296         }else{
30297            e.stopPropagation();
30298            this.slideOut();
30299         }
30300     },
30301
30302     /**
30303      * Collapses this region.
30304      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30305      */
30306     collapse : function(skipAnim){
30307         if(this.collapsed) return;
30308         this.collapsed = true;
30309         if(this.split){
30310             this.split.el.hide();
30311         }
30312         if(this.config.animate && skipAnim !== true){
30313             this.fireEvent("invalidated", this);
30314             this.animateCollapse();
30315         }else{
30316             this.el.setLocation(-20000,-20000);
30317             this.el.hide();
30318             this.collapsedEl.show();
30319             this.fireEvent("collapsed", this);
30320             this.fireEvent("invalidated", this);
30321         }
30322     },
30323
30324     animateCollapse : function(){
30325         // overridden
30326     },
30327
30328     /**
30329      * Expands this region if it was previously collapsed.
30330      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30331      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30332      */
30333     expand : function(e, skipAnim){
30334         if(e) e.stopPropagation();
30335         if(!this.collapsed || this.el.hasActiveFx()) return;
30336         if(this.isSlid){
30337             this.afterSlideIn();
30338             skipAnim = true;
30339         }
30340         this.collapsed = false;
30341         if(this.config.animate && skipAnim !== true){
30342             this.animateExpand();
30343         }else{
30344             this.el.show();
30345             if(this.split){
30346                 this.split.el.show();
30347             }
30348             this.collapsedEl.setLocation(-2000,-2000);
30349             this.collapsedEl.hide();
30350             this.fireEvent("invalidated", this);
30351             this.fireEvent("expanded", this);
30352         }
30353     },
30354
30355     animateExpand : function(){
30356         // overridden
30357     },
30358
30359     initTabs : function()
30360     {
30361         this.bodyEl.setStyle("overflow", "hidden");
30362         var ts = new Roo.TabPanel(
30363                 this.bodyEl.dom,
30364                 {
30365                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30366                     disableTooltips: this.config.disableTabTips,
30367                     toolbar : this.config.toolbar
30368                 }
30369         );
30370         if(this.config.hideTabs){
30371             ts.stripWrap.setDisplayed(false);
30372         }
30373         this.tabs = ts;
30374         ts.resizeTabs = this.config.resizeTabs === true;
30375         ts.minTabWidth = this.config.minTabWidth || 40;
30376         ts.maxTabWidth = this.config.maxTabWidth || 250;
30377         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30378         ts.monitorResize = false;
30379         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30380         ts.bodyEl.addClass('x-layout-tabs-body');
30381         this.panels.each(this.initPanelAsTab, this);
30382     },
30383
30384     initPanelAsTab : function(panel){
30385         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30386                     this.config.closeOnTab && panel.isClosable());
30387         if(panel.tabTip !== undefined){
30388             ti.setTooltip(panel.tabTip);
30389         }
30390         ti.on("activate", function(){
30391               this.setActivePanel(panel);
30392         }, this);
30393         if(this.config.closeOnTab){
30394             ti.on("beforeclose", function(t, e){
30395                 e.cancel = true;
30396                 this.remove(panel);
30397             }, this);
30398         }
30399         return ti;
30400     },
30401
30402     updatePanelTitle : function(panel, title){
30403         if(this.activePanel == panel){
30404             this.updateTitle(title);
30405         }
30406         if(this.tabs){
30407             var ti = this.tabs.getTab(panel.getEl().id);
30408             ti.setText(title);
30409             if(panel.tabTip !== undefined){
30410                 ti.setTooltip(panel.tabTip);
30411             }
30412         }
30413     },
30414
30415     updateTitle : function(title){
30416         if(this.titleTextEl && !this.config.title){
30417             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30418         }
30419     },
30420
30421     setActivePanel : function(panel){
30422         panel = this.getPanel(panel);
30423         if(this.activePanel && this.activePanel != panel){
30424             this.activePanel.setActiveState(false);
30425         }
30426         this.activePanel = panel;
30427         panel.setActiveState(true);
30428         if(this.panelSize){
30429             panel.setSize(this.panelSize.width, this.panelSize.height);
30430         }
30431         if(this.closeBtn){
30432             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30433         }
30434         this.updateTitle(panel.getTitle());
30435         if(this.tabs){
30436             this.fireEvent("invalidated", this);
30437         }
30438         this.fireEvent("panelactivated", this, panel);
30439     },
30440
30441     /**
30442      * Shows the specified panel.
30443      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30444      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30445      */
30446     showPanel : function(panel){
30447         if(panel = this.getPanel(panel)){
30448             if(this.tabs){
30449                 var tab = this.tabs.getTab(panel.getEl().id);
30450                 if(tab.isHidden()){
30451                     this.tabs.unhideTab(tab.id);
30452                 }
30453                 tab.activate();
30454             }else{
30455                 this.setActivePanel(panel);
30456             }
30457         }
30458         return panel;
30459     },
30460
30461     /**
30462      * Get the active panel for this region.
30463      * @return {Roo.ContentPanel} The active panel or null
30464      */
30465     getActivePanel : function(){
30466         return this.activePanel;
30467     },
30468
30469     validateVisibility : function(){
30470         if(this.panels.getCount() < 1){
30471             this.updateTitle("&#160;");
30472             this.closeBtn.hide();
30473             this.hide();
30474         }else{
30475             if(!this.isVisible()){
30476                 this.show();
30477             }
30478         }
30479     },
30480
30481     /**
30482      * Adds the passed ContentPanel(s) to this region.
30483      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30484      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30485      */
30486     add : function(panel){
30487         if(arguments.length > 1){
30488             for(var i = 0, len = arguments.length; i < len; i++) {
30489                 this.add(arguments[i]);
30490             }
30491             return null;
30492         }
30493         if(this.hasPanel(panel)){
30494             this.showPanel(panel);
30495             return panel;
30496         }
30497         panel.setRegion(this);
30498         this.panels.add(panel);
30499         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30500             this.bodyEl.dom.appendChild(panel.getEl().dom);
30501             if(panel.background !== true){
30502                 this.setActivePanel(panel);
30503             }
30504             this.fireEvent("paneladded", this, panel);
30505             return panel;
30506         }
30507         if(!this.tabs){
30508             this.initTabs();
30509         }else{
30510             this.initPanelAsTab(panel);
30511         }
30512         if(panel.background !== true){
30513             this.tabs.activate(panel.getEl().id);
30514         }
30515         this.fireEvent("paneladded", this, panel);
30516         return panel;
30517     },
30518
30519     /**
30520      * Hides the tab for the specified panel.
30521      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30522      */
30523     hidePanel : function(panel){
30524         if(this.tabs && (panel = this.getPanel(panel))){
30525             this.tabs.hideTab(panel.getEl().id);
30526         }
30527     },
30528
30529     /**
30530      * Unhides the tab for a previously hidden panel.
30531      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30532      */
30533     unhidePanel : function(panel){
30534         if(this.tabs && (panel = this.getPanel(panel))){
30535             this.tabs.unhideTab(panel.getEl().id);
30536         }
30537     },
30538
30539     clearPanels : function(){
30540         while(this.panels.getCount() > 0){
30541              this.remove(this.panels.first());
30542         }
30543     },
30544
30545     /**
30546      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30547      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30548      * @param {Boolean} preservePanel Overrides the config preservePanel option
30549      * @return {Roo.ContentPanel} The panel that was removed
30550      */
30551     remove : function(panel, preservePanel){
30552         panel = this.getPanel(panel);
30553         if(!panel){
30554             return null;
30555         }
30556         var e = {};
30557         this.fireEvent("beforeremove", this, panel, e);
30558         if(e.cancel === true){
30559             return null;
30560         }
30561         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30562         var panelId = panel.getId();
30563         this.panels.removeKey(panelId);
30564         if(preservePanel){
30565             document.body.appendChild(panel.getEl().dom);
30566         }
30567         if(this.tabs){
30568             this.tabs.removeTab(panel.getEl().id);
30569         }else if (!preservePanel){
30570             this.bodyEl.dom.removeChild(panel.getEl().dom);
30571         }
30572         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30573             var p = this.panels.first();
30574             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30575             tempEl.appendChild(p.getEl().dom);
30576             this.bodyEl.update("");
30577             this.bodyEl.dom.appendChild(p.getEl().dom);
30578             tempEl = null;
30579             this.updateTitle(p.getTitle());
30580             this.tabs = null;
30581             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30582             this.setActivePanel(p);
30583         }
30584         panel.setRegion(null);
30585         if(this.activePanel == panel){
30586             this.activePanel = null;
30587         }
30588         if(this.config.autoDestroy !== false && preservePanel !== true){
30589             try{panel.destroy();}catch(e){}
30590         }
30591         this.fireEvent("panelremoved", this, panel);
30592         return panel;
30593     },
30594
30595     /**
30596      * Returns the TabPanel component used by this region
30597      * @return {Roo.TabPanel}
30598      */
30599     getTabs : function(){
30600         return this.tabs;
30601     },
30602
30603     createTool : function(parentEl, className){
30604         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30605             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30606         btn.addClassOnOver("x-layout-tools-button-over");
30607         return btn;
30608     }
30609 });/*
30610  * Based on:
30611  * Ext JS Library 1.1.1
30612  * Copyright(c) 2006-2007, Ext JS, LLC.
30613  *
30614  * Originally Released Under LGPL - original licence link has changed is not relivant.
30615  *
30616  * Fork - LGPL
30617  * <script type="text/javascript">
30618  */
30619  
30620
30621
30622 /**
30623  * @class Roo.SplitLayoutRegion
30624  * @extends Roo.LayoutRegion
30625  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30626  */
30627 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30628     this.cursor = cursor;
30629     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30630 };
30631
30632 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30633     splitTip : "Drag to resize.",
30634     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30635     useSplitTips : false,
30636
30637     applyConfig : function(config){
30638         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30639         if(config.split){
30640             if(!this.split){
30641                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30642                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30643                 /** The SplitBar for this region 
30644                 * @type Roo.SplitBar */
30645                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30646                 this.split.on("moved", this.onSplitMove, this);
30647                 this.split.useShim = config.useShim === true;
30648                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30649                 if(this.useSplitTips){
30650                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30651                 }
30652                 if(config.collapsible){
30653                     this.split.el.on("dblclick", this.collapse,  this);
30654                 }
30655             }
30656             if(typeof config.minSize != "undefined"){
30657                 this.split.minSize = config.minSize;
30658             }
30659             if(typeof config.maxSize != "undefined"){
30660                 this.split.maxSize = config.maxSize;
30661             }
30662             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30663                 this.hideSplitter();
30664             }
30665         }
30666     },
30667
30668     getHMaxSize : function(){
30669          var cmax = this.config.maxSize || 10000;
30670          var center = this.mgr.getRegion("center");
30671          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30672     },
30673
30674     getVMaxSize : function(){
30675          var cmax = this.config.maxSize || 10000;
30676          var center = this.mgr.getRegion("center");
30677          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30678     },
30679
30680     onSplitMove : function(split, newSize){
30681         this.fireEvent("resized", this, newSize);
30682     },
30683     
30684     /** 
30685      * Returns the {@link Roo.SplitBar} for this region.
30686      * @return {Roo.SplitBar}
30687      */
30688     getSplitBar : function(){
30689         return this.split;
30690     },
30691     
30692     hide : function(){
30693         this.hideSplitter();
30694         Roo.SplitLayoutRegion.superclass.hide.call(this);
30695     },
30696
30697     hideSplitter : function(){
30698         if(this.split){
30699             this.split.el.setLocation(-2000,-2000);
30700             this.split.el.hide();
30701         }
30702     },
30703
30704     show : function(){
30705         if(this.split){
30706             this.split.el.show();
30707         }
30708         Roo.SplitLayoutRegion.superclass.show.call(this);
30709     },
30710     
30711     beforeSlide: function(){
30712         if(Roo.isGecko){// firefox overflow auto bug workaround
30713             this.bodyEl.clip();
30714             if(this.tabs) this.tabs.bodyEl.clip();
30715             if(this.activePanel){
30716                 this.activePanel.getEl().clip();
30717                 
30718                 if(this.activePanel.beforeSlide){
30719                     this.activePanel.beforeSlide();
30720                 }
30721             }
30722         }
30723     },
30724     
30725     afterSlide : function(){
30726         if(Roo.isGecko){// firefox overflow auto bug workaround
30727             this.bodyEl.unclip();
30728             if(this.tabs) this.tabs.bodyEl.unclip();
30729             if(this.activePanel){
30730                 this.activePanel.getEl().unclip();
30731                 if(this.activePanel.afterSlide){
30732                     this.activePanel.afterSlide();
30733                 }
30734             }
30735         }
30736     },
30737
30738     initAutoHide : function(){
30739         if(this.autoHide !== false){
30740             if(!this.autoHideHd){
30741                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30742                 this.autoHideHd = {
30743                     "mouseout": function(e){
30744                         if(!e.within(this.el, true)){
30745                             st.delay(500);
30746                         }
30747                     },
30748                     "mouseover" : function(e){
30749                         st.cancel();
30750                     },
30751                     scope : this
30752                 };
30753             }
30754             this.el.on(this.autoHideHd);
30755         }
30756     },
30757
30758     clearAutoHide : function(){
30759         if(this.autoHide !== false){
30760             this.el.un("mouseout", this.autoHideHd.mouseout);
30761             this.el.un("mouseover", this.autoHideHd.mouseover);
30762         }
30763     },
30764
30765     clearMonitor : function(){
30766         Roo.get(document).un("click", this.slideInIf, this);
30767     },
30768
30769     // these names are backwards but not changed for compat
30770     slideOut : function(){
30771         if(this.isSlid || this.el.hasActiveFx()){
30772             return;
30773         }
30774         this.isSlid = true;
30775         if(this.collapseBtn){
30776             this.collapseBtn.hide();
30777         }
30778         this.closeBtnState = this.closeBtn.getStyle('display');
30779         this.closeBtn.hide();
30780         if(this.stickBtn){
30781             this.stickBtn.show();
30782         }
30783         this.el.show();
30784         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30785         this.beforeSlide();
30786         this.el.setStyle("z-index", 10001);
30787         this.el.slideIn(this.getSlideAnchor(), {
30788             callback: function(){
30789                 this.afterSlide();
30790                 this.initAutoHide();
30791                 Roo.get(document).on("click", this.slideInIf, this);
30792                 this.fireEvent("slideshow", this);
30793             },
30794             scope: this,
30795             block: true
30796         });
30797     },
30798
30799     afterSlideIn : function(){
30800         this.clearAutoHide();
30801         this.isSlid = false;
30802         this.clearMonitor();
30803         this.el.setStyle("z-index", "");
30804         if(this.collapseBtn){
30805             this.collapseBtn.show();
30806         }
30807         this.closeBtn.setStyle('display', this.closeBtnState);
30808         if(this.stickBtn){
30809             this.stickBtn.hide();
30810         }
30811         this.fireEvent("slidehide", this);
30812     },
30813
30814     slideIn : function(cb){
30815         if(!this.isSlid || this.el.hasActiveFx()){
30816             Roo.callback(cb);
30817             return;
30818         }
30819         this.isSlid = false;
30820         this.beforeSlide();
30821         this.el.slideOut(this.getSlideAnchor(), {
30822             callback: function(){
30823                 this.el.setLeftTop(-10000, -10000);
30824                 this.afterSlide();
30825                 this.afterSlideIn();
30826                 Roo.callback(cb);
30827             },
30828             scope: this,
30829             block: true
30830         });
30831     },
30832     
30833     slideInIf : function(e){
30834         if(!e.within(this.el)){
30835             this.slideIn();
30836         }
30837     },
30838
30839     animateCollapse : function(){
30840         this.beforeSlide();
30841         this.el.setStyle("z-index", 20000);
30842         var anchor = this.getSlideAnchor();
30843         this.el.slideOut(anchor, {
30844             callback : function(){
30845                 this.el.setStyle("z-index", "");
30846                 this.collapsedEl.slideIn(anchor, {duration:.3});
30847                 this.afterSlide();
30848                 this.el.setLocation(-10000,-10000);
30849                 this.el.hide();
30850                 this.fireEvent("collapsed", this);
30851             },
30852             scope: this,
30853             block: true
30854         });
30855     },
30856
30857     animateExpand : function(){
30858         this.beforeSlide();
30859         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30860         this.el.setStyle("z-index", 20000);
30861         this.collapsedEl.hide({
30862             duration:.1
30863         });
30864         this.el.slideIn(this.getSlideAnchor(), {
30865             callback : function(){
30866                 this.el.setStyle("z-index", "");
30867                 this.afterSlide();
30868                 if(this.split){
30869                     this.split.el.show();
30870                 }
30871                 this.fireEvent("invalidated", this);
30872                 this.fireEvent("expanded", this);
30873             },
30874             scope: this,
30875             block: true
30876         });
30877     },
30878
30879     anchors : {
30880         "west" : "left",
30881         "east" : "right",
30882         "north" : "top",
30883         "south" : "bottom"
30884     },
30885
30886     sanchors : {
30887         "west" : "l",
30888         "east" : "r",
30889         "north" : "t",
30890         "south" : "b"
30891     },
30892
30893     canchors : {
30894         "west" : "tl-tr",
30895         "east" : "tr-tl",
30896         "north" : "tl-bl",
30897         "south" : "bl-tl"
30898     },
30899
30900     getAnchor : function(){
30901         return this.anchors[this.position];
30902     },
30903
30904     getCollapseAnchor : function(){
30905         return this.canchors[this.position];
30906     },
30907
30908     getSlideAnchor : function(){
30909         return this.sanchors[this.position];
30910     },
30911
30912     getAlignAdj : function(){
30913         var cm = this.cmargins;
30914         switch(this.position){
30915             case "west":
30916                 return [0, 0];
30917             break;
30918             case "east":
30919                 return [0, 0];
30920             break;
30921             case "north":
30922                 return [0, 0];
30923             break;
30924             case "south":
30925                 return [0, 0];
30926             break;
30927         }
30928     },
30929
30930     getExpandAdj : function(){
30931         var c = this.collapsedEl, cm = this.cmargins;
30932         switch(this.position){
30933             case "west":
30934                 return [-(cm.right+c.getWidth()+cm.left), 0];
30935             break;
30936             case "east":
30937                 return [cm.right+c.getWidth()+cm.left, 0];
30938             break;
30939             case "north":
30940                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30941             break;
30942             case "south":
30943                 return [0, cm.top+cm.bottom+c.getHeight()];
30944             break;
30945         }
30946     }
30947 });/*
30948  * Based on:
30949  * Ext JS Library 1.1.1
30950  * Copyright(c) 2006-2007, Ext JS, LLC.
30951  *
30952  * Originally Released Under LGPL - original licence link has changed is not relivant.
30953  *
30954  * Fork - LGPL
30955  * <script type="text/javascript">
30956  */
30957 /*
30958  * These classes are private internal classes
30959  */
30960 Roo.CenterLayoutRegion = function(mgr, config){
30961     Roo.LayoutRegion.call(this, mgr, config, "center");
30962     this.visible = true;
30963     this.minWidth = config.minWidth || 20;
30964     this.minHeight = config.minHeight || 20;
30965 };
30966
30967 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30968     hide : function(){
30969         // center panel can't be hidden
30970     },
30971     
30972     show : function(){
30973         // center panel can't be hidden
30974     },
30975     
30976     getMinWidth: function(){
30977         return this.minWidth;
30978     },
30979     
30980     getMinHeight: function(){
30981         return this.minHeight;
30982     }
30983 });
30984
30985
30986 Roo.NorthLayoutRegion = function(mgr, config){
30987     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30988     if(this.split){
30989         this.split.placement = Roo.SplitBar.TOP;
30990         this.split.orientation = Roo.SplitBar.VERTICAL;
30991         this.split.el.addClass("x-layout-split-v");
30992     }
30993     var size = config.initialSize || config.height;
30994     if(typeof size != "undefined"){
30995         this.el.setHeight(size);
30996     }
30997 };
30998 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30999     orientation: Roo.SplitBar.VERTICAL,
31000     getBox : function(){
31001         if(this.collapsed){
31002             return this.collapsedEl.getBox();
31003         }
31004         var box = this.el.getBox();
31005         if(this.split){
31006             box.height += this.split.el.getHeight();
31007         }
31008         return box;
31009     },
31010     
31011     updateBox : function(box){
31012         if(this.split && !this.collapsed){
31013             box.height -= this.split.el.getHeight();
31014             this.split.el.setLeft(box.x);
31015             this.split.el.setTop(box.y+box.height);
31016             this.split.el.setWidth(box.width);
31017         }
31018         if(this.collapsed){
31019             this.updateBody(box.width, null);
31020         }
31021         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31022     }
31023 });
31024
31025 Roo.SouthLayoutRegion = function(mgr, config){
31026     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31027     if(this.split){
31028         this.split.placement = Roo.SplitBar.BOTTOM;
31029         this.split.orientation = Roo.SplitBar.VERTICAL;
31030         this.split.el.addClass("x-layout-split-v");
31031     }
31032     var size = config.initialSize || config.height;
31033     if(typeof size != "undefined"){
31034         this.el.setHeight(size);
31035     }
31036 };
31037 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31038     orientation: Roo.SplitBar.VERTICAL,
31039     getBox : function(){
31040         if(this.collapsed){
31041             return this.collapsedEl.getBox();
31042         }
31043         var box = this.el.getBox();
31044         if(this.split){
31045             var sh = this.split.el.getHeight();
31046             box.height += sh;
31047             box.y -= sh;
31048         }
31049         return box;
31050     },
31051     
31052     updateBox : function(box){
31053         if(this.split && !this.collapsed){
31054             var sh = this.split.el.getHeight();
31055             box.height -= sh;
31056             box.y += sh;
31057             this.split.el.setLeft(box.x);
31058             this.split.el.setTop(box.y-sh);
31059             this.split.el.setWidth(box.width);
31060         }
31061         if(this.collapsed){
31062             this.updateBody(box.width, null);
31063         }
31064         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31065     }
31066 });
31067
31068 Roo.EastLayoutRegion = function(mgr, config){
31069     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31070     if(this.split){
31071         this.split.placement = Roo.SplitBar.RIGHT;
31072         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31073         this.split.el.addClass("x-layout-split-h");
31074     }
31075     var size = config.initialSize || config.width;
31076     if(typeof size != "undefined"){
31077         this.el.setWidth(size);
31078     }
31079 };
31080 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31081     orientation: Roo.SplitBar.HORIZONTAL,
31082     getBox : function(){
31083         if(this.collapsed){
31084             return this.collapsedEl.getBox();
31085         }
31086         var box = this.el.getBox();
31087         if(this.split){
31088             var sw = this.split.el.getWidth();
31089             box.width += sw;
31090             box.x -= sw;
31091         }
31092         return box;
31093     },
31094
31095     updateBox : function(box){
31096         if(this.split && !this.collapsed){
31097             var sw = this.split.el.getWidth();
31098             box.width -= sw;
31099             this.split.el.setLeft(box.x);
31100             this.split.el.setTop(box.y);
31101             this.split.el.setHeight(box.height);
31102             box.x += sw;
31103         }
31104         if(this.collapsed){
31105             this.updateBody(null, box.height);
31106         }
31107         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31108     }
31109 });
31110
31111 Roo.WestLayoutRegion = function(mgr, config){
31112     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31113     if(this.split){
31114         this.split.placement = Roo.SplitBar.LEFT;
31115         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31116         this.split.el.addClass("x-layout-split-h");
31117     }
31118     var size = config.initialSize || config.width;
31119     if(typeof size != "undefined"){
31120         this.el.setWidth(size);
31121     }
31122 };
31123 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31124     orientation: Roo.SplitBar.HORIZONTAL,
31125     getBox : function(){
31126         if(this.collapsed){
31127             return this.collapsedEl.getBox();
31128         }
31129         var box = this.el.getBox();
31130         if(this.split){
31131             box.width += this.split.el.getWidth();
31132         }
31133         return box;
31134     },
31135     
31136     updateBox : function(box){
31137         if(this.split && !this.collapsed){
31138             var sw = this.split.el.getWidth();
31139             box.width -= sw;
31140             this.split.el.setLeft(box.x+box.width);
31141             this.split.el.setTop(box.y);
31142             this.split.el.setHeight(box.height);
31143         }
31144         if(this.collapsed){
31145             this.updateBody(null, box.height);
31146         }
31147         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31148     }
31149 });
31150 /*
31151  * Based on:
31152  * Ext JS Library 1.1.1
31153  * Copyright(c) 2006-2007, Ext JS, LLC.
31154  *
31155  * Originally Released Under LGPL - original licence link has changed is not relivant.
31156  *
31157  * Fork - LGPL
31158  * <script type="text/javascript">
31159  */
31160  
31161  
31162 /*
31163  * Private internal class for reading and applying state
31164  */
31165 Roo.LayoutStateManager = function(layout){
31166      // default empty state
31167      this.state = {
31168         north: {},
31169         south: {},
31170         east: {},
31171         west: {}       
31172     };
31173 };
31174
31175 Roo.LayoutStateManager.prototype = {
31176     init : function(layout, provider){
31177         this.provider = provider;
31178         var state = provider.get(layout.id+"-layout-state");
31179         if(state){
31180             var wasUpdating = layout.isUpdating();
31181             if(!wasUpdating){
31182                 layout.beginUpdate();
31183             }
31184             for(var key in state){
31185                 if(typeof state[key] != "function"){
31186                     var rstate = state[key];
31187                     var r = layout.getRegion(key);
31188                     if(r && rstate){
31189                         if(rstate.size){
31190                             r.resizeTo(rstate.size);
31191                         }
31192                         if(rstate.collapsed == true){
31193                             r.collapse(true);
31194                         }else{
31195                             r.expand(null, true);
31196                         }
31197                     }
31198                 }
31199             }
31200             if(!wasUpdating){
31201                 layout.endUpdate();
31202             }
31203             this.state = state; 
31204         }
31205         this.layout = layout;
31206         layout.on("regionresized", this.onRegionResized, this);
31207         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31208         layout.on("regionexpanded", this.onRegionExpanded, this);
31209     },
31210     
31211     storeState : function(){
31212         this.provider.set(this.layout.id+"-layout-state", this.state);
31213     },
31214     
31215     onRegionResized : function(region, newSize){
31216         this.state[region.getPosition()].size = newSize;
31217         this.storeState();
31218     },
31219     
31220     onRegionCollapsed : function(region){
31221         this.state[region.getPosition()].collapsed = true;
31222         this.storeState();
31223     },
31224     
31225     onRegionExpanded : function(region){
31226         this.state[region.getPosition()].collapsed = false;
31227         this.storeState();
31228     }
31229 };/*
31230  * Based on:
31231  * Ext JS Library 1.1.1
31232  * Copyright(c) 2006-2007, Ext JS, LLC.
31233  *
31234  * Originally Released Under LGPL - original licence link has changed is not relivant.
31235  *
31236  * Fork - LGPL
31237  * <script type="text/javascript">
31238  */
31239 /**
31240  * @class Roo.ContentPanel
31241  * @extends Roo.util.Observable
31242  * A basic ContentPanel element.
31243  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31244  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31245  * @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
31246  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31247  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31248  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31249  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31250  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31251  * @cfg {String} title          The title for this panel
31252  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31253  * @cfg {String} url            Calls {@link #setUrl} with this value
31254  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31255  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31256  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31257  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31258
31259  * @constructor
31260  * Create a new ContentPanel.
31261  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31262  * @param {String/Object} config A string to set only the title or a config object
31263  * @param {String} content (optional) Set the HTML content for this panel
31264  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31265  */
31266 Roo.ContentPanel = function(el, config, content){
31267     
31268      
31269     /*
31270     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31271         config = el;
31272         el = Roo.id();
31273     }
31274     if (config && config.parentLayout) { 
31275         el = config.parentLayout.el.createChild(); 
31276     }
31277     */
31278     if(el.autoCreate){ // xtype is available if this is called from factory
31279         config = el;
31280         el = Roo.id();
31281     }
31282     this.el = Roo.get(el);
31283     if(!this.el && config && config.autoCreate){
31284         if(typeof config.autoCreate == "object"){
31285             if(!config.autoCreate.id){
31286                 config.autoCreate.id = config.id||el;
31287             }
31288             this.el = Roo.DomHelper.append(document.body,
31289                         config.autoCreate, true);
31290         }else{
31291             this.el = Roo.DomHelper.append(document.body,
31292                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31293         }
31294     }
31295     this.closable = false;
31296     this.loaded = false;
31297     this.active = false;
31298     if(typeof config == "string"){
31299         this.title = config;
31300     }else{
31301         Roo.apply(this, config);
31302     }
31303     
31304     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31305         this.wrapEl = this.el.wrap();    
31306         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31307         
31308     }
31309     
31310     
31311     
31312     if(this.resizeEl){
31313         this.resizeEl = Roo.get(this.resizeEl, true);
31314     }else{
31315         this.resizeEl = this.el;
31316     }
31317     this.addEvents({
31318         /**
31319          * @event activate
31320          * Fires when this panel is activated. 
31321          * @param {Roo.ContentPanel} this
31322          */
31323         "activate" : true,
31324         /**
31325          * @event deactivate
31326          * Fires when this panel is activated. 
31327          * @param {Roo.ContentPanel} this
31328          */
31329         "deactivate" : true,
31330
31331         /**
31332          * @event resize
31333          * Fires when this panel is resized if fitToFrame is true.
31334          * @param {Roo.ContentPanel} this
31335          * @param {Number} width The width after any component adjustments
31336          * @param {Number} height The height after any component adjustments
31337          */
31338         "resize" : true,
31339         
31340          /**
31341          * @event render
31342          * Fires when this tab is created
31343          * @param {Roo.ContentPanel} this
31344          */
31345         "render" : true
31346         
31347         
31348         
31349     });
31350     if(this.autoScroll){
31351         this.resizeEl.setStyle("overflow", "auto");
31352     } else {
31353         // fix randome scrolling
31354         this.el.on('scroll', function() {
31355             Roo.log('fix random scolling');
31356             this.scrollTo('top',0); 
31357         });
31358     }
31359     content = content || this.content;
31360     if(content){
31361         this.setContent(content);
31362     }
31363     if(config && config.url){
31364         this.setUrl(this.url, this.params, this.loadOnce);
31365     }
31366     
31367     
31368     
31369     Roo.ContentPanel.superclass.constructor.call(this);
31370     
31371     this.fireEvent('render', this);
31372 };
31373
31374 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31375     tabTip:'',
31376     setRegion : function(region){
31377         this.region = region;
31378         if(region){
31379            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31380         }else{
31381            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31382         } 
31383     },
31384     
31385     /**
31386      * Returns the toolbar for this Panel if one was configured. 
31387      * @return {Roo.Toolbar} 
31388      */
31389     getToolbar : function(){
31390         return this.toolbar;
31391     },
31392     
31393     setActiveState : function(active){
31394         this.active = active;
31395         if(!active){
31396             this.fireEvent("deactivate", this);
31397         }else{
31398             this.fireEvent("activate", this);
31399         }
31400     },
31401     /**
31402      * Updates this panel's element
31403      * @param {String} content The new content
31404      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31405     */
31406     setContent : function(content, loadScripts){
31407         this.el.update(content, loadScripts);
31408     },
31409
31410     ignoreResize : function(w, h){
31411         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31412             return true;
31413         }else{
31414             this.lastSize = {width: w, height: h};
31415             return false;
31416         }
31417     },
31418     /**
31419      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31420      * @return {Roo.UpdateManager} The UpdateManager
31421      */
31422     getUpdateManager : function(){
31423         return this.el.getUpdateManager();
31424     },
31425      /**
31426      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31427      * @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:
31428 <pre><code>
31429 panel.load({
31430     url: "your-url.php",
31431     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31432     callback: yourFunction,
31433     scope: yourObject, //(optional scope)
31434     discardUrl: false,
31435     nocache: false,
31436     text: "Loading...",
31437     timeout: 30,
31438     scripts: false
31439 });
31440 </code></pre>
31441      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31442      * 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.
31443      * @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}
31444      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31445      * @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.
31446      * @return {Roo.ContentPanel} this
31447      */
31448     load : function(){
31449         var um = this.el.getUpdateManager();
31450         um.update.apply(um, arguments);
31451         return this;
31452     },
31453
31454
31455     /**
31456      * 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.
31457      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31458      * @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)
31459      * @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)
31460      * @return {Roo.UpdateManager} The UpdateManager
31461      */
31462     setUrl : function(url, params, loadOnce){
31463         if(this.refreshDelegate){
31464             this.removeListener("activate", this.refreshDelegate);
31465         }
31466         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31467         this.on("activate", this.refreshDelegate);
31468         return this.el.getUpdateManager();
31469     },
31470     
31471     _handleRefresh : function(url, params, loadOnce){
31472         if(!loadOnce || !this.loaded){
31473             var updater = this.el.getUpdateManager();
31474             updater.update(url, params, this._setLoaded.createDelegate(this));
31475         }
31476     },
31477     
31478     _setLoaded : function(){
31479         this.loaded = true;
31480     }, 
31481     
31482     /**
31483      * Returns this panel's id
31484      * @return {String} 
31485      */
31486     getId : function(){
31487         return this.el.id;
31488     },
31489     
31490     /** 
31491      * Returns this panel's element - used by regiosn to add.
31492      * @return {Roo.Element} 
31493      */
31494     getEl : function(){
31495         return this.wrapEl || this.el;
31496     },
31497     
31498     adjustForComponents : function(width, height){
31499         if(this.resizeEl != this.el){
31500             width -= this.el.getFrameWidth('lr');
31501             height -= this.el.getFrameWidth('tb');
31502         }
31503         if(this.toolbar){
31504             var te = this.toolbar.getEl();
31505             height -= te.getHeight();
31506             te.setWidth(width);
31507         }
31508         if(this.adjustments){
31509             width += this.adjustments[0];
31510             height += this.adjustments[1];
31511         }
31512         return {"width": width, "height": height};
31513     },
31514     
31515     setSize : function(width, height){
31516         if(this.fitToFrame && !this.ignoreResize(width, height)){
31517             if(this.fitContainer && this.resizeEl != this.el){
31518                 this.el.setSize(width, height);
31519             }
31520             var size = this.adjustForComponents(width, height);
31521             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31522             this.fireEvent('resize', this, size.width, size.height);
31523         }
31524     },
31525     
31526     /**
31527      * Returns this panel's title
31528      * @return {String} 
31529      */
31530     getTitle : function(){
31531         return this.title;
31532     },
31533     
31534     /**
31535      * Set this panel's title
31536      * @param {String} title
31537      */
31538     setTitle : function(title){
31539         this.title = title;
31540         if(this.region){
31541             this.region.updatePanelTitle(this, title);
31542         }
31543     },
31544     
31545     /**
31546      * Returns true is this panel was configured to be closable
31547      * @return {Boolean} 
31548      */
31549     isClosable : function(){
31550         return this.closable;
31551     },
31552     
31553     beforeSlide : function(){
31554         this.el.clip();
31555         this.resizeEl.clip();
31556     },
31557     
31558     afterSlide : function(){
31559         this.el.unclip();
31560         this.resizeEl.unclip();
31561     },
31562     
31563     /**
31564      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31565      *   Will fail silently if the {@link #setUrl} method has not been called.
31566      *   This does not activate the panel, just updates its content.
31567      */
31568     refresh : function(){
31569         if(this.refreshDelegate){
31570            this.loaded = false;
31571            this.refreshDelegate();
31572         }
31573     },
31574     
31575     /**
31576      * Destroys this panel
31577      */
31578     destroy : function(){
31579         this.el.removeAllListeners();
31580         var tempEl = document.createElement("span");
31581         tempEl.appendChild(this.el.dom);
31582         tempEl.innerHTML = "";
31583         this.el.remove();
31584         this.el = null;
31585     },
31586     
31587     /**
31588      * form - if the content panel contains a form - this is a reference to it.
31589      * @type {Roo.form.Form}
31590      */
31591     form : false,
31592     /**
31593      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31594      *    This contains a reference to it.
31595      * @type {Roo.View}
31596      */
31597     view : false,
31598     
31599       /**
31600      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31601      * <pre><code>
31602
31603 layout.addxtype({
31604        xtype : 'Form',
31605        items: [ .... ]
31606    }
31607 );
31608
31609 </code></pre>
31610      * @param {Object} cfg Xtype definition of item to add.
31611      */
31612     
31613     addxtype : function(cfg) {
31614         // add form..
31615         if (cfg.xtype.match(/^Form$/)) {
31616             var el = this.el.createChild();
31617
31618             this.form = new  Roo.form.Form(cfg);
31619             
31620             
31621             if ( this.form.allItems.length) this.form.render(el.dom);
31622             return this.form;
31623         }
31624         // should only have one of theses..
31625         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31626             // views..
31627             cfg.el = this.el.appendChild(document.createElement("div"));
31628             // factory?
31629             
31630             var ret = new Roo.factory(cfg);
31631             ret.render && ret.render(false, ''); // render blank..
31632             this.view = ret;
31633             return ret;
31634         }
31635         return false;
31636     }
31637 });
31638
31639 /**
31640  * @class Roo.GridPanel
31641  * @extends Roo.ContentPanel
31642  * @constructor
31643  * Create a new GridPanel.
31644  * @param {Roo.grid.Grid} grid The grid for this panel
31645  * @param {String/Object} config A string to set only the panel's title, or a config object
31646  */
31647 Roo.GridPanel = function(grid, config){
31648     
31649   
31650     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31651         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31652         
31653     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31654     
31655     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31656     
31657     if(this.toolbar){
31658         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31659     }
31660     // xtype created footer. - not sure if will work as we normally have to render first..
31661     if (this.footer && !this.footer.el && this.footer.xtype) {
31662         
31663         this.footer.container = this.grid.getView().getFooterPanel(true);
31664         this.footer.dataSource = this.grid.dataSource;
31665         this.footer = Roo.factory(this.footer, Roo);
31666         
31667     }
31668     
31669     grid.monitorWindowResize = false; // turn off autosizing
31670     grid.autoHeight = false;
31671     grid.autoWidth = false;
31672     this.grid = grid;
31673     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31674 };
31675
31676 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31677     getId : function(){
31678         return this.grid.id;
31679     },
31680     
31681     /**
31682      * Returns the grid for this panel
31683      * @return {Roo.grid.Grid} 
31684      */
31685     getGrid : function(){
31686         return this.grid;    
31687     },
31688     
31689     setSize : function(width, height){
31690         if(!this.ignoreResize(width, height)){
31691             var grid = this.grid;
31692             var size = this.adjustForComponents(width, height);
31693             grid.getGridEl().setSize(size.width, size.height);
31694             grid.autoSize();
31695         }
31696     },
31697     
31698     beforeSlide : function(){
31699         this.grid.getView().scroller.clip();
31700     },
31701     
31702     afterSlide : function(){
31703         this.grid.getView().scroller.unclip();
31704     },
31705     
31706     destroy : function(){
31707         this.grid.destroy();
31708         delete this.grid;
31709         Roo.GridPanel.superclass.destroy.call(this); 
31710     }
31711 });
31712
31713
31714 /**
31715  * @class Roo.NestedLayoutPanel
31716  * @extends Roo.ContentPanel
31717  * @constructor
31718  * Create a new NestedLayoutPanel.
31719  * 
31720  * 
31721  * @param {Roo.BorderLayout} layout The layout for this panel
31722  * @param {String/Object} config A string to set only the title or a config object
31723  */
31724 Roo.NestedLayoutPanel = function(layout, config)
31725 {
31726     // construct with only one argument..
31727     /* FIXME - implement nicer consturctors
31728     if (layout.layout) {
31729         config = layout;
31730         layout = config.layout;
31731         delete config.layout;
31732     }
31733     if (layout.xtype && !layout.getEl) {
31734         // then layout needs constructing..
31735         layout = Roo.factory(layout, Roo);
31736     }
31737     */
31738     
31739     
31740     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31741     
31742     layout.monitorWindowResize = false; // turn off autosizing
31743     this.layout = layout;
31744     this.layout.getEl().addClass("x-layout-nested-layout");
31745     
31746     
31747     
31748     
31749 };
31750
31751 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31752
31753     setSize : function(width, height){
31754         if(!this.ignoreResize(width, height)){
31755             var size = this.adjustForComponents(width, height);
31756             var el = this.layout.getEl();
31757             el.setSize(size.width, size.height);
31758             var touch = el.dom.offsetWidth;
31759             this.layout.layout();
31760             // ie requires a double layout on the first pass
31761             if(Roo.isIE && !this.initialized){
31762                 this.initialized = true;
31763                 this.layout.layout();
31764             }
31765         }
31766     },
31767     
31768     // activate all subpanels if not currently active..
31769     
31770     setActiveState : function(active){
31771         this.active = active;
31772         if(!active){
31773             this.fireEvent("deactivate", this);
31774             return;
31775         }
31776         
31777         this.fireEvent("activate", this);
31778         // not sure if this should happen before or after..
31779         if (!this.layout) {
31780             return; // should not happen..
31781         }
31782         var reg = false;
31783         for (var r in this.layout.regions) {
31784             reg = this.layout.getRegion(r);
31785             if (reg.getActivePanel()) {
31786                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31787                 reg.setActivePanel(reg.getActivePanel());
31788                 continue;
31789             }
31790             if (!reg.panels.length) {
31791                 continue;
31792             }
31793             reg.showPanel(reg.getPanel(0));
31794         }
31795         
31796         
31797         
31798         
31799     },
31800     
31801     /**
31802      * Returns the nested BorderLayout for this panel
31803      * @return {Roo.BorderLayout} 
31804      */
31805     getLayout : function(){
31806         return this.layout;
31807     },
31808     
31809      /**
31810      * Adds a xtype elements to the layout of the nested panel
31811      * <pre><code>
31812
31813 panel.addxtype({
31814        xtype : 'ContentPanel',
31815        region: 'west',
31816        items: [ .... ]
31817    }
31818 );
31819
31820 panel.addxtype({
31821         xtype : 'NestedLayoutPanel',
31822         region: 'west',
31823         layout: {
31824            center: { },
31825            west: { }   
31826         },
31827         items : [ ... list of content panels or nested layout panels.. ]
31828    }
31829 );
31830 </code></pre>
31831      * @param {Object} cfg Xtype definition of item to add.
31832      */
31833     addxtype : function(cfg) {
31834         return this.layout.addxtype(cfg);
31835     
31836     }
31837 });
31838
31839 Roo.ScrollPanel = function(el, config, content){
31840     config = config || {};
31841     config.fitToFrame = true;
31842     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31843     
31844     this.el.dom.style.overflow = "hidden";
31845     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31846     this.el.removeClass("x-layout-inactive-content");
31847     this.el.on("mousewheel", this.onWheel, this);
31848
31849     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31850     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31851     up.unselectable(); down.unselectable();
31852     up.on("click", this.scrollUp, this);
31853     down.on("click", this.scrollDown, this);
31854     up.addClassOnOver("x-scroller-btn-over");
31855     down.addClassOnOver("x-scroller-btn-over");
31856     up.addClassOnClick("x-scroller-btn-click");
31857     down.addClassOnClick("x-scroller-btn-click");
31858     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31859
31860     this.resizeEl = this.el;
31861     this.el = wrap; this.up = up; this.down = down;
31862 };
31863
31864 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31865     increment : 100,
31866     wheelIncrement : 5,
31867     scrollUp : function(){
31868         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31869     },
31870
31871     scrollDown : function(){
31872         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31873     },
31874
31875     afterScroll : function(){
31876         var el = this.resizeEl;
31877         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31878         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31879         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31880     },
31881
31882     setSize : function(){
31883         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31884         this.afterScroll();
31885     },
31886
31887     onWheel : function(e){
31888         var d = e.getWheelDelta();
31889         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31890         this.afterScroll();
31891         e.stopEvent();
31892     },
31893
31894     setContent : function(content, loadScripts){
31895         this.resizeEl.update(content, loadScripts);
31896     }
31897
31898 });
31899
31900
31901
31902
31903
31904
31905
31906
31907
31908 /**
31909  * @class Roo.TreePanel
31910  * @extends Roo.ContentPanel
31911  * @constructor
31912  * Create a new TreePanel. - defaults to fit/scoll contents.
31913  * @param {String/Object} config A string to set only the panel's title, or a config object
31914  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31915  */
31916 Roo.TreePanel = function(config){
31917     var el = config.el;
31918     var tree = config.tree;
31919     delete config.tree; 
31920     delete config.el; // hopefull!
31921     
31922     // wrapper for IE7 strict & safari scroll issue
31923     
31924     var treeEl = el.createChild();
31925     config.resizeEl = treeEl;
31926     
31927     
31928     
31929     Roo.TreePanel.superclass.constructor.call(this, el, config);
31930  
31931  
31932     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31933     //console.log(tree);
31934     this.on('activate', function()
31935     {
31936         if (this.tree.rendered) {
31937             return;
31938         }
31939         //console.log('render tree');
31940         this.tree.render();
31941     });
31942     
31943     this.on('resize',  function (cp, w, h) {
31944             this.tree.innerCt.setWidth(w);
31945             this.tree.innerCt.setHeight(h);
31946             this.tree.innerCt.setStyle('overflow-y', 'auto');
31947     });
31948
31949         
31950     
31951 };
31952
31953 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31954     fitToFrame : true,
31955     autoScroll : true
31956 });
31957
31958
31959
31960
31961
31962
31963
31964
31965
31966
31967
31968 /*
31969  * Based on:
31970  * Ext JS Library 1.1.1
31971  * Copyright(c) 2006-2007, Ext JS, LLC.
31972  *
31973  * Originally Released Under LGPL - original licence link has changed is not relivant.
31974  *
31975  * Fork - LGPL
31976  * <script type="text/javascript">
31977  */
31978  
31979
31980 /**
31981  * @class Roo.ReaderLayout
31982  * @extends Roo.BorderLayout
31983  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31984  * center region containing two nested regions (a top one for a list view and one for item preview below),
31985  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31986  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31987  * expedites the setup of the overall layout and regions for this common application style.
31988  * Example:
31989  <pre><code>
31990 var reader = new Roo.ReaderLayout();
31991 var CP = Roo.ContentPanel;  // shortcut for adding
31992
31993 reader.beginUpdate();
31994 reader.add("north", new CP("north", "North"));
31995 reader.add("west", new CP("west", {title: "West"}));
31996 reader.add("east", new CP("east", {title: "East"}));
31997
31998 reader.regions.listView.add(new CP("listView", "List"));
31999 reader.regions.preview.add(new CP("preview", "Preview"));
32000 reader.endUpdate();
32001 </code></pre>
32002 * @constructor
32003 * Create a new ReaderLayout
32004 * @param {Object} config Configuration options
32005 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32006 * document.body if omitted)
32007 */
32008 Roo.ReaderLayout = function(config, renderTo){
32009     var c = config || {size:{}};
32010     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32011         north: c.north !== false ? Roo.apply({
32012             split:false,
32013             initialSize: 32,
32014             titlebar: false
32015         }, c.north) : false,
32016         west: c.west !== false ? Roo.apply({
32017             split:true,
32018             initialSize: 200,
32019             minSize: 175,
32020             maxSize: 400,
32021             titlebar: true,
32022             collapsible: true,
32023             animate: true,
32024             margins:{left:5,right:0,bottom:5,top:5},
32025             cmargins:{left:5,right:5,bottom:5,top:5}
32026         }, c.west) : false,
32027         east: c.east !== false ? Roo.apply({
32028             split:true,
32029             initialSize: 200,
32030             minSize: 175,
32031             maxSize: 400,
32032             titlebar: true,
32033             collapsible: true,
32034             animate: true,
32035             margins:{left:0,right:5,bottom:5,top:5},
32036             cmargins:{left:5,right:5,bottom:5,top:5}
32037         }, c.east) : false,
32038         center: Roo.apply({
32039             tabPosition: 'top',
32040             autoScroll:false,
32041             closeOnTab: true,
32042             titlebar:false,
32043             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32044         }, c.center)
32045     });
32046
32047     this.el.addClass('x-reader');
32048
32049     this.beginUpdate();
32050
32051     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32052         south: c.preview !== false ? Roo.apply({
32053             split:true,
32054             initialSize: 200,
32055             minSize: 100,
32056             autoScroll:true,
32057             collapsible:true,
32058             titlebar: true,
32059             cmargins:{top:5,left:0, right:0, bottom:0}
32060         }, c.preview) : false,
32061         center: Roo.apply({
32062             autoScroll:false,
32063             titlebar:false,
32064             minHeight:200
32065         }, c.listView)
32066     });
32067     this.add('center', new Roo.NestedLayoutPanel(inner,
32068             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32069
32070     this.endUpdate();
32071
32072     this.regions.preview = inner.getRegion('south');
32073     this.regions.listView = inner.getRegion('center');
32074 };
32075
32076 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32077  * Based on:
32078  * Ext JS Library 1.1.1
32079  * Copyright(c) 2006-2007, Ext JS, LLC.
32080  *
32081  * Originally Released Under LGPL - original licence link has changed is not relivant.
32082  *
32083  * Fork - LGPL
32084  * <script type="text/javascript">
32085  */
32086  
32087 /**
32088  * @class Roo.grid.Grid
32089  * @extends Roo.util.Observable
32090  * This class represents the primary interface of a component based grid control.
32091  * <br><br>Usage:<pre><code>
32092  var grid = new Roo.grid.Grid("my-container-id", {
32093      ds: myDataStore,
32094      cm: myColModel,
32095      selModel: mySelectionModel,
32096      autoSizeColumns: true,
32097      monitorWindowResize: false,
32098      trackMouseOver: true
32099  });
32100  // set any options
32101  grid.render();
32102  * </code></pre>
32103  * <b>Common Problems:</b><br/>
32104  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32105  * element will correct this<br/>
32106  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32107  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32108  * are unpredictable.<br/>
32109  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32110  * grid to calculate dimensions/offsets.<br/>
32111   * @constructor
32112  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32113  * The container MUST have some type of size defined for the grid to fill. The container will be
32114  * automatically set to position relative if it isn't already.
32115  * @param {Object} config A config object that sets properties on this grid.
32116  */
32117 Roo.grid.Grid = function(container, config){
32118         // initialize the container
32119         this.container = Roo.get(container);
32120         this.container.update("");
32121         this.container.setStyle("overflow", "hidden");
32122     this.container.addClass('x-grid-container');
32123
32124     this.id = this.container.id;
32125
32126     Roo.apply(this, config);
32127     // check and correct shorthanded configs
32128     if(this.ds){
32129         this.dataSource = this.ds;
32130         delete this.ds;
32131     }
32132     if(this.cm){
32133         this.colModel = this.cm;
32134         delete this.cm;
32135     }
32136     if(this.sm){
32137         this.selModel = this.sm;
32138         delete this.sm;
32139     }
32140
32141     if (this.selModel) {
32142         this.selModel = Roo.factory(this.selModel, Roo.grid);
32143         this.sm = this.selModel;
32144         this.sm.xmodule = this.xmodule || false;
32145     }
32146     if (typeof(this.colModel.config) == 'undefined') {
32147         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32148         this.cm = this.colModel;
32149         this.cm.xmodule = this.xmodule || false;
32150     }
32151     if (this.dataSource) {
32152         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32153         this.ds = this.dataSource;
32154         this.ds.xmodule = this.xmodule || false;
32155          
32156     }
32157     
32158     
32159     
32160     if(this.width){
32161         this.container.setWidth(this.width);
32162     }
32163
32164     if(this.height){
32165         this.container.setHeight(this.height);
32166     }
32167     /** @private */
32168         this.addEvents({
32169         // raw events
32170         /**
32171          * @event click
32172          * The raw click event for the entire grid.
32173          * @param {Roo.EventObject} e
32174          */
32175         "click" : true,
32176         /**
32177          * @event dblclick
32178          * The raw dblclick event for the entire grid.
32179          * @param {Roo.EventObject} e
32180          */
32181         "dblclick" : true,
32182         /**
32183          * @event contextmenu
32184          * The raw contextmenu event for the entire grid.
32185          * @param {Roo.EventObject} e
32186          */
32187         "contextmenu" : true,
32188         /**
32189          * @event mousedown
32190          * The raw mousedown event for the entire grid.
32191          * @param {Roo.EventObject} e
32192          */
32193         "mousedown" : true,
32194         /**
32195          * @event mouseup
32196          * The raw mouseup event for the entire grid.
32197          * @param {Roo.EventObject} e
32198          */
32199         "mouseup" : true,
32200         /**
32201          * @event mouseover
32202          * The raw mouseover event for the entire grid.
32203          * @param {Roo.EventObject} e
32204          */
32205         "mouseover" : true,
32206         /**
32207          * @event mouseout
32208          * The raw mouseout event for the entire grid.
32209          * @param {Roo.EventObject} e
32210          */
32211         "mouseout" : true,
32212         /**
32213          * @event keypress
32214          * The raw keypress event for the entire grid.
32215          * @param {Roo.EventObject} e
32216          */
32217         "keypress" : true,
32218         /**
32219          * @event keydown
32220          * The raw keydown event for the entire grid.
32221          * @param {Roo.EventObject} e
32222          */
32223         "keydown" : true,
32224
32225         // custom events
32226
32227         /**
32228          * @event cellclick
32229          * Fires when a cell is clicked
32230          * @param {Grid} this
32231          * @param {Number} rowIndex
32232          * @param {Number} columnIndex
32233          * @param {Roo.EventObject} e
32234          */
32235         "cellclick" : true,
32236         /**
32237          * @event celldblclick
32238          * Fires when a cell is double clicked
32239          * @param {Grid} this
32240          * @param {Number} rowIndex
32241          * @param {Number} columnIndex
32242          * @param {Roo.EventObject} e
32243          */
32244         "celldblclick" : true,
32245         /**
32246          * @event rowclick
32247          * Fires when a row is clicked
32248          * @param {Grid} this
32249          * @param {Number} rowIndex
32250          * @param {Roo.EventObject} e
32251          */
32252         "rowclick" : true,
32253         /**
32254          * @event rowdblclick
32255          * Fires when a row is double clicked
32256          * @param {Grid} this
32257          * @param {Number} rowIndex
32258          * @param {Roo.EventObject} e
32259          */
32260         "rowdblclick" : true,
32261         /**
32262          * @event headerclick
32263          * Fires when a header is clicked
32264          * @param {Grid} this
32265          * @param {Number} columnIndex
32266          * @param {Roo.EventObject} e
32267          */
32268         "headerclick" : true,
32269         /**
32270          * @event headerdblclick
32271          * Fires when a header cell is double clicked
32272          * @param {Grid} this
32273          * @param {Number} columnIndex
32274          * @param {Roo.EventObject} e
32275          */
32276         "headerdblclick" : true,
32277         /**
32278          * @event rowcontextmenu
32279          * Fires when a row is right clicked
32280          * @param {Grid} this
32281          * @param {Number} rowIndex
32282          * @param {Roo.EventObject} e
32283          */
32284         "rowcontextmenu" : true,
32285         /**
32286          * @event cellcontextmenu
32287          * Fires when a cell is right clicked
32288          * @param {Grid} this
32289          * @param {Number} rowIndex
32290          * @param {Number} cellIndex
32291          * @param {Roo.EventObject} e
32292          */
32293          "cellcontextmenu" : true,
32294         /**
32295          * @event headercontextmenu
32296          * Fires when a header is right clicked
32297          * @param {Grid} this
32298          * @param {Number} columnIndex
32299          * @param {Roo.EventObject} e
32300          */
32301         "headercontextmenu" : true,
32302         /**
32303          * @event bodyscroll
32304          * Fires when the body element is scrolled
32305          * @param {Number} scrollLeft
32306          * @param {Number} scrollTop
32307          */
32308         "bodyscroll" : true,
32309         /**
32310          * @event columnresize
32311          * Fires when the user resizes a column
32312          * @param {Number} columnIndex
32313          * @param {Number} newSize
32314          */
32315         "columnresize" : true,
32316         /**
32317          * @event columnmove
32318          * Fires when the user moves a column
32319          * @param {Number} oldIndex
32320          * @param {Number} newIndex
32321          */
32322         "columnmove" : true,
32323         /**
32324          * @event startdrag
32325          * Fires when row(s) start being dragged
32326          * @param {Grid} this
32327          * @param {Roo.GridDD} dd The drag drop object
32328          * @param {event} e The raw browser event
32329          */
32330         "startdrag" : true,
32331         /**
32332          * @event enddrag
32333          * Fires when a drag operation is complete
32334          * @param {Grid} this
32335          * @param {Roo.GridDD} dd The drag drop object
32336          * @param {event} e The raw browser event
32337          */
32338         "enddrag" : true,
32339         /**
32340          * @event dragdrop
32341          * Fires when dragged row(s) are dropped on a valid DD target
32342          * @param {Grid} this
32343          * @param {Roo.GridDD} dd The drag drop object
32344          * @param {String} targetId The target drag drop object
32345          * @param {event} e The raw browser event
32346          */
32347         "dragdrop" : true,
32348         /**
32349          * @event dragover
32350          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32351          * @param {Grid} this
32352          * @param {Roo.GridDD} dd The drag drop object
32353          * @param {String} targetId The target drag drop object
32354          * @param {event} e The raw browser event
32355          */
32356         "dragover" : true,
32357         /**
32358          * @event dragenter
32359          *  Fires when the dragged row(s) first cross another DD target while being dragged
32360          * @param {Grid} this
32361          * @param {Roo.GridDD} dd The drag drop object
32362          * @param {String} targetId The target drag drop object
32363          * @param {event} e The raw browser event
32364          */
32365         "dragenter" : true,
32366         /**
32367          * @event dragout
32368          * Fires when the dragged row(s) leave another DD target while being dragged
32369          * @param {Grid} this
32370          * @param {Roo.GridDD} dd The drag drop object
32371          * @param {String} targetId The target drag drop object
32372          * @param {event} e The raw browser event
32373          */
32374         "dragout" : true,
32375         /**
32376          * @event rowclass
32377          * Fires when a row is rendered, so you can change add a style to it.
32378          * @param {GridView} gridview   The grid view
32379          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32380          */
32381         'rowclass' : true,
32382
32383         /**
32384          * @event render
32385          * Fires when the grid is rendered
32386          * @param {Grid} grid
32387          */
32388         'render' : true
32389     });
32390
32391     Roo.grid.Grid.superclass.constructor.call(this);
32392 };
32393 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32394     
32395     /**
32396      * @cfg {String} ddGroup - drag drop group.
32397      */
32398
32399     /**
32400      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32401      */
32402     minColumnWidth : 25,
32403
32404     /**
32405      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32406      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32407      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32408      */
32409     autoSizeColumns : false,
32410
32411     /**
32412      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32413      */
32414     autoSizeHeaders : true,
32415
32416     /**
32417      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32418      */
32419     monitorWindowResize : true,
32420
32421     /**
32422      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32423      * rows measured to get a columns size. Default is 0 (all rows).
32424      */
32425     maxRowsToMeasure : 0,
32426
32427     /**
32428      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32429      */
32430     trackMouseOver : true,
32431
32432     /**
32433     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32434     */
32435     
32436     /**
32437     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32438     */
32439     enableDragDrop : false,
32440     
32441     /**
32442     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32443     */
32444     enableColumnMove : true,
32445     
32446     /**
32447     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32448     */
32449     enableColumnHide : true,
32450     
32451     /**
32452     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32453     */
32454     enableRowHeightSync : false,
32455     
32456     /**
32457     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32458     */
32459     stripeRows : true,
32460     
32461     /**
32462     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32463     */
32464     autoHeight : false,
32465
32466     /**
32467      * @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.
32468      */
32469     autoExpandColumn : false,
32470
32471     /**
32472     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32473     * Default is 50.
32474     */
32475     autoExpandMin : 50,
32476
32477     /**
32478     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32479     */
32480     autoExpandMax : 1000,
32481
32482     /**
32483     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32484     */
32485     view : null,
32486
32487     /**
32488     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32489     */
32490     loadMask : false,
32491     /**
32492     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32493     */
32494     dropTarget: false,
32495     
32496    
32497     
32498     // private
32499     rendered : false,
32500
32501     /**
32502     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32503     * of a fixed width. Default is false.
32504     */
32505     /**
32506     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32507     */
32508     /**
32509      * Called once after all setup has been completed and the grid is ready to be rendered.
32510      * @return {Roo.grid.Grid} this
32511      */
32512     render : function()
32513     {
32514         var c = this.container;
32515         // try to detect autoHeight/width mode
32516         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32517             this.autoHeight = true;
32518         }
32519         var view = this.getView();
32520         view.init(this);
32521
32522         c.on("click", this.onClick, this);
32523         c.on("dblclick", this.onDblClick, this);
32524         c.on("contextmenu", this.onContextMenu, this);
32525         c.on("keydown", this.onKeyDown, this);
32526
32527         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32528
32529         this.getSelectionModel().init(this);
32530
32531         view.render();
32532
32533         if(this.loadMask){
32534             this.loadMask = new Roo.LoadMask(this.container,
32535                     Roo.apply({store:this.dataSource}, this.loadMask));
32536         }
32537         
32538         
32539         if (this.toolbar && this.toolbar.xtype) {
32540             this.toolbar.container = this.getView().getHeaderPanel(true);
32541             this.toolbar = new Roo.Toolbar(this.toolbar);
32542         }
32543         if (this.footer && this.footer.xtype) {
32544             this.footer.dataSource = this.getDataSource();
32545             this.footer.container = this.getView().getFooterPanel(true);
32546             this.footer = Roo.factory(this.footer, Roo);
32547         }
32548         if (this.dropTarget && this.dropTarget.xtype) {
32549             delete this.dropTarget.xtype;
32550             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32551         }
32552         
32553         
32554         this.rendered = true;
32555         this.fireEvent('render', this);
32556         return this;
32557     },
32558
32559         /**
32560          * Reconfigures the grid to use a different Store and Column Model.
32561          * The View will be bound to the new objects and refreshed.
32562          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32563          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32564          */
32565     reconfigure : function(dataSource, colModel){
32566         if(this.loadMask){
32567             this.loadMask.destroy();
32568             this.loadMask = new Roo.LoadMask(this.container,
32569                     Roo.apply({store:dataSource}, this.loadMask));
32570         }
32571         this.view.bind(dataSource, colModel);
32572         this.dataSource = dataSource;
32573         this.colModel = colModel;
32574         this.view.refresh(true);
32575     },
32576
32577     // private
32578     onKeyDown : function(e){
32579         this.fireEvent("keydown", e);
32580     },
32581
32582     /**
32583      * Destroy this grid.
32584      * @param {Boolean} removeEl True to remove the element
32585      */
32586     destroy : function(removeEl, keepListeners){
32587         if(this.loadMask){
32588             this.loadMask.destroy();
32589         }
32590         var c = this.container;
32591         c.removeAllListeners();
32592         this.view.destroy();
32593         this.colModel.purgeListeners();
32594         if(!keepListeners){
32595             this.purgeListeners();
32596         }
32597         c.update("");
32598         if(removeEl === true){
32599             c.remove();
32600         }
32601     },
32602
32603     // private
32604     processEvent : function(name, e){
32605         this.fireEvent(name, e);
32606         var t = e.getTarget();
32607         var v = this.view;
32608         var header = v.findHeaderIndex(t);
32609         if(header !== false){
32610             this.fireEvent("header" + name, this, header, e);
32611         }else{
32612             var row = v.findRowIndex(t);
32613             var cell = v.findCellIndex(t);
32614             if(row !== false){
32615                 this.fireEvent("row" + name, this, row, e);
32616                 if(cell !== false){
32617                     this.fireEvent("cell" + name, this, row, cell, e);
32618                 }
32619             }
32620         }
32621     },
32622
32623     // private
32624     onClick : function(e){
32625         this.processEvent("click", e);
32626     },
32627
32628     // private
32629     onContextMenu : function(e, t){
32630         this.processEvent("contextmenu", e);
32631     },
32632
32633     // private
32634     onDblClick : function(e){
32635         this.processEvent("dblclick", e);
32636     },
32637
32638     // private
32639     walkCells : function(row, col, step, fn, scope){
32640         var cm = this.colModel, clen = cm.getColumnCount();
32641         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32642         if(step < 0){
32643             if(col < 0){
32644                 row--;
32645                 first = false;
32646             }
32647             while(row >= 0){
32648                 if(!first){
32649                     col = clen-1;
32650                 }
32651                 first = false;
32652                 while(col >= 0){
32653                     if(fn.call(scope || this, row, col, cm) === true){
32654                         return [row, col];
32655                     }
32656                     col--;
32657                 }
32658                 row--;
32659             }
32660         } else {
32661             if(col >= clen){
32662                 row++;
32663                 first = false;
32664             }
32665             while(row < rlen){
32666                 if(!first){
32667                     col = 0;
32668                 }
32669                 first = false;
32670                 while(col < clen){
32671                     if(fn.call(scope || this, row, col, cm) === true){
32672                         return [row, col];
32673                     }
32674                     col++;
32675                 }
32676                 row++;
32677             }
32678         }
32679         return null;
32680     },
32681
32682     // private
32683     getSelections : function(){
32684         return this.selModel.getSelections();
32685     },
32686
32687     /**
32688      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32689      * but if manual update is required this method will initiate it.
32690      */
32691     autoSize : function(){
32692         if(this.rendered){
32693             this.view.layout();
32694             if(this.view.adjustForScroll){
32695                 this.view.adjustForScroll();
32696             }
32697         }
32698     },
32699
32700     /**
32701      * Returns the grid's underlying element.
32702      * @return {Element} The element
32703      */
32704     getGridEl : function(){
32705         return this.container;
32706     },
32707
32708     // private for compatibility, overridden by editor grid
32709     stopEditing : function(){},
32710
32711     /**
32712      * Returns the grid's SelectionModel.
32713      * @return {SelectionModel}
32714      */
32715     getSelectionModel : function(){
32716         if(!this.selModel){
32717             this.selModel = new Roo.grid.RowSelectionModel();
32718         }
32719         return this.selModel;
32720     },
32721
32722     /**
32723      * Returns the grid's DataSource.
32724      * @return {DataSource}
32725      */
32726     getDataSource : function(){
32727         return this.dataSource;
32728     },
32729
32730     /**
32731      * Returns the grid's ColumnModel.
32732      * @return {ColumnModel}
32733      */
32734     getColumnModel : function(){
32735         return this.colModel;
32736     },
32737
32738     /**
32739      * Returns the grid's GridView object.
32740      * @return {GridView}
32741      */
32742     getView : function(){
32743         if(!this.view){
32744             this.view = new Roo.grid.GridView(this.viewConfig);
32745         }
32746         return this.view;
32747     },
32748     /**
32749      * Called to get grid's drag proxy text, by default returns this.ddText.
32750      * @return {String}
32751      */
32752     getDragDropText : function(){
32753         var count = this.selModel.getCount();
32754         return String.format(this.ddText, count, count == 1 ? '' : 's');
32755     }
32756 });
32757 /**
32758  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32759  * %0 is replaced with the number of selected rows.
32760  * @type String
32761  */
32762 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32763  * Based on:
32764  * Ext JS Library 1.1.1
32765  * Copyright(c) 2006-2007, Ext JS, LLC.
32766  *
32767  * Originally Released Under LGPL - original licence link has changed is not relivant.
32768  *
32769  * Fork - LGPL
32770  * <script type="text/javascript">
32771  */
32772  
32773 Roo.grid.AbstractGridView = function(){
32774         this.grid = null;
32775         
32776         this.events = {
32777             "beforerowremoved" : true,
32778             "beforerowsinserted" : true,
32779             "beforerefresh" : true,
32780             "rowremoved" : true,
32781             "rowsinserted" : true,
32782             "rowupdated" : true,
32783             "refresh" : true
32784         };
32785     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32786 };
32787
32788 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32789     rowClass : "x-grid-row",
32790     cellClass : "x-grid-cell",
32791     tdClass : "x-grid-td",
32792     hdClass : "x-grid-hd",
32793     splitClass : "x-grid-hd-split",
32794     
32795         init: function(grid){
32796         this.grid = grid;
32797                 var cid = this.grid.getGridEl().id;
32798         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32799         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32800         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32801         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32802         },
32803         
32804         getColumnRenderers : function(){
32805         var renderers = [];
32806         var cm = this.grid.colModel;
32807         var colCount = cm.getColumnCount();
32808         for(var i = 0; i < colCount; i++){
32809             renderers[i] = cm.getRenderer(i);
32810         }
32811         return renderers;
32812     },
32813     
32814     getColumnIds : function(){
32815         var ids = [];
32816         var cm = this.grid.colModel;
32817         var colCount = cm.getColumnCount();
32818         for(var i = 0; i < colCount; i++){
32819             ids[i] = cm.getColumnId(i);
32820         }
32821         return ids;
32822     },
32823     
32824     getDataIndexes : function(){
32825         if(!this.indexMap){
32826             this.indexMap = this.buildIndexMap();
32827         }
32828         return this.indexMap.colToData;
32829     },
32830     
32831     getColumnIndexByDataIndex : function(dataIndex){
32832         if(!this.indexMap){
32833             this.indexMap = this.buildIndexMap();
32834         }
32835         return this.indexMap.dataToCol[dataIndex];
32836     },
32837     
32838     /**
32839      * Set a css style for a column dynamically. 
32840      * @param {Number} colIndex The index of the column
32841      * @param {String} name The css property name
32842      * @param {String} value The css value
32843      */
32844     setCSSStyle : function(colIndex, name, value){
32845         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32846         Roo.util.CSS.updateRule(selector, name, value);
32847     },
32848     
32849     generateRules : function(cm){
32850         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32851         Roo.util.CSS.removeStyleSheet(rulesId);
32852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32853             var cid = cm.getColumnId(i);
32854             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32855                          this.tdSelector, cid, " {\n}\n",
32856                          this.hdSelector, cid, " {\n}\n",
32857                          this.splitSelector, cid, " {\n}\n");
32858         }
32859         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32860     }
32861 });/*
32862  * Based on:
32863  * Ext JS Library 1.1.1
32864  * Copyright(c) 2006-2007, Ext JS, LLC.
32865  *
32866  * Originally Released Under LGPL - original licence link has changed is not relivant.
32867  *
32868  * Fork - LGPL
32869  * <script type="text/javascript">
32870  */
32871
32872 // private
32873 // This is a support class used internally by the Grid components
32874 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32875     this.grid = grid;
32876     this.view = grid.getView();
32877     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32878     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32879     if(hd2){
32880         this.setHandleElId(Roo.id(hd));
32881         this.setOuterHandleElId(Roo.id(hd2));
32882     }
32883     this.scroll = false;
32884 };
32885 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32886     maxDragWidth: 120,
32887     getDragData : function(e){
32888         var t = Roo.lib.Event.getTarget(e);
32889         var h = this.view.findHeaderCell(t);
32890         if(h){
32891             return {ddel: h.firstChild, header:h};
32892         }
32893         return false;
32894     },
32895
32896     onInitDrag : function(e){
32897         this.view.headersDisabled = true;
32898         var clone = this.dragData.ddel.cloneNode(true);
32899         clone.id = Roo.id();
32900         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32901         this.proxy.update(clone);
32902         return true;
32903     },
32904
32905     afterValidDrop : function(){
32906         var v = this.view;
32907         setTimeout(function(){
32908             v.headersDisabled = false;
32909         }, 50);
32910     },
32911
32912     afterInvalidDrop : function(){
32913         var v = this.view;
32914         setTimeout(function(){
32915             v.headersDisabled = false;
32916         }, 50);
32917     }
32918 });
32919 /*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929 // private
32930 // This is a support class used internally by the Grid components
32931 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32932     this.grid = grid;
32933     this.view = grid.getView();
32934     // split the proxies so they don't interfere with mouse events
32935     this.proxyTop = Roo.DomHelper.append(document.body, {
32936         cls:"col-move-top", html:"&#160;"
32937     }, true);
32938     this.proxyBottom = Roo.DomHelper.append(document.body, {
32939         cls:"col-move-bottom", html:"&#160;"
32940     }, true);
32941     this.proxyTop.hide = this.proxyBottom.hide = function(){
32942         this.setLeftTop(-100,-100);
32943         this.setStyle("visibility", "hidden");
32944     };
32945     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32946     // temporarily disabled
32947     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32948     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32949 };
32950 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32951     proxyOffsets : [-4, -9],
32952     fly: Roo.Element.fly,
32953
32954     getTargetFromEvent : function(e){
32955         var t = Roo.lib.Event.getTarget(e);
32956         var cindex = this.view.findCellIndex(t);
32957         if(cindex !== false){
32958             return this.view.getHeaderCell(cindex);
32959         }
32960         return null;
32961     },
32962
32963     nextVisible : function(h){
32964         var v = this.view, cm = this.grid.colModel;
32965         h = h.nextSibling;
32966         while(h){
32967             if(!cm.isHidden(v.getCellIndex(h))){
32968                 return h;
32969             }
32970             h = h.nextSibling;
32971         }
32972         return null;
32973     },
32974
32975     prevVisible : function(h){
32976         var v = this.view, cm = this.grid.colModel;
32977         h = h.prevSibling;
32978         while(h){
32979             if(!cm.isHidden(v.getCellIndex(h))){
32980                 return h;
32981             }
32982             h = h.prevSibling;
32983         }
32984         return null;
32985     },
32986
32987     positionIndicator : function(h, n, e){
32988         var x = Roo.lib.Event.getPageX(e);
32989         var r = Roo.lib.Dom.getRegion(n.firstChild);
32990         var px, pt, py = r.top + this.proxyOffsets[1];
32991         if((r.right - x) <= (r.right-r.left)/2){
32992             px = r.right+this.view.borderWidth;
32993             pt = "after";
32994         }else{
32995             px = r.left;
32996             pt = "before";
32997         }
32998         var oldIndex = this.view.getCellIndex(h);
32999         var newIndex = this.view.getCellIndex(n);
33000
33001         if(this.grid.colModel.isFixed(newIndex)){
33002             return false;
33003         }
33004
33005         var locked = this.grid.colModel.isLocked(newIndex);
33006
33007         if(pt == "after"){
33008             newIndex++;
33009         }
33010         if(oldIndex < newIndex){
33011             newIndex--;
33012         }
33013         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33014             return false;
33015         }
33016         px +=  this.proxyOffsets[0];
33017         this.proxyTop.setLeftTop(px, py);
33018         this.proxyTop.show();
33019         if(!this.bottomOffset){
33020             this.bottomOffset = this.view.mainHd.getHeight();
33021         }
33022         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33023         this.proxyBottom.show();
33024         return pt;
33025     },
33026
33027     onNodeEnter : function(n, dd, e, data){
33028         if(data.header != n){
33029             this.positionIndicator(data.header, n, e);
33030         }
33031     },
33032
33033     onNodeOver : function(n, dd, e, data){
33034         var result = false;
33035         if(data.header != n){
33036             result = this.positionIndicator(data.header, n, e);
33037         }
33038         if(!result){
33039             this.proxyTop.hide();
33040             this.proxyBottom.hide();
33041         }
33042         return result ? this.dropAllowed : this.dropNotAllowed;
33043     },
33044
33045     onNodeOut : function(n, dd, e, data){
33046         this.proxyTop.hide();
33047         this.proxyBottom.hide();
33048     },
33049
33050     onNodeDrop : function(n, dd, e, data){
33051         var h = data.header;
33052         if(h != n){
33053             var cm = this.grid.colModel;
33054             var x = Roo.lib.Event.getPageX(e);
33055             var r = Roo.lib.Dom.getRegion(n.firstChild);
33056             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33057             var oldIndex = this.view.getCellIndex(h);
33058             var newIndex = this.view.getCellIndex(n);
33059             var locked = cm.isLocked(newIndex);
33060             if(pt == "after"){
33061                 newIndex++;
33062             }
33063             if(oldIndex < newIndex){
33064                 newIndex--;
33065             }
33066             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33067                 return false;
33068             }
33069             cm.setLocked(oldIndex, locked, true);
33070             cm.moveColumn(oldIndex, newIndex);
33071             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33072             return true;
33073         }
33074         return false;
33075     }
33076 });
33077 /*
33078  * Based on:
33079  * Ext JS Library 1.1.1
33080  * Copyright(c) 2006-2007, Ext JS, LLC.
33081  *
33082  * Originally Released Under LGPL - original licence link has changed is not relivant.
33083  *
33084  * Fork - LGPL
33085  * <script type="text/javascript">
33086  */
33087   
33088 /**
33089  * @class Roo.grid.GridView
33090  * @extends Roo.util.Observable
33091  *
33092  * @constructor
33093  * @param {Object} config
33094  */
33095 Roo.grid.GridView = function(config){
33096     Roo.grid.GridView.superclass.constructor.call(this);
33097     this.el = null;
33098
33099     Roo.apply(this, config);
33100 };
33101
33102 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33103
33104     /**
33105      * Override this function to apply custom css classes to rows during rendering
33106      * @param {Record} record The record
33107      * @param {Number} index
33108      * @method getRowClass
33109      */
33110     rowClass : "x-grid-row",
33111
33112     cellClass : "x-grid-col",
33113
33114     tdClass : "x-grid-td",
33115
33116     hdClass : "x-grid-hd",
33117
33118     splitClass : "x-grid-split",
33119
33120     sortClasses : ["sort-asc", "sort-desc"],
33121
33122     enableMoveAnim : false,
33123
33124     hlColor: "C3DAF9",
33125
33126     dh : Roo.DomHelper,
33127
33128     fly : Roo.Element.fly,
33129
33130     css : Roo.util.CSS,
33131
33132     borderWidth: 1,
33133
33134     splitOffset: 3,
33135
33136     scrollIncrement : 22,
33137
33138     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33139
33140     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33141
33142     bind : function(ds, cm){
33143         if(this.ds){
33144             this.ds.un("load", this.onLoad, this);
33145             this.ds.un("datachanged", this.onDataChange, this);
33146             this.ds.un("add", this.onAdd, this);
33147             this.ds.un("remove", this.onRemove, this);
33148             this.ds.un("update", this.onUpdate, this);
33149             this.ds.un("clear", this.onClear, this);
33150         }
33151         if(ds){
33152             ds.on("load", this.onLoad, this);
33153             ds.on("datachanged", this.onDataChange, this);
33154             ds.on("add", this.onAdd, this);
33155             ds.on("remove", this.onRemove, this);
33156             ds.on("update", this.onUpdate, this);
33157             ds.on("clear", this.onClear, this);
33158         }
33159         this.ds = ds;
33160
33161         if(this.cm){
33162             this.cm.un("widthchange", this.onColWidthChange, this);
33163             this.cm.un("headerchange", this.onHeaderChange, this);
33164             this.cm.un("hiddenchange", this.onHiddenChange, this);
33165             this.cm.un("columnmoved", this.onColumnMove, this);
33166             this.cm.un("columnlockchange", this.onColumnLock, this);
33167         }
33168         if(cm){
33169             this.generateRules(cm);
33170             cm.on("widthchange", this.onColWidthChange, this);
33171             cm.on("headerchange", this.onHeaderChange, this);
33172             cm.on("hiddenchange", this.onHiddenChange, this);
33173             cm.on("columnmoved", this.onColumnMove, this);
33174             cm.on("columnlockchange", this.onColumnLock, this);
33175         }
33176         this.cm = cm;
33177     },
33178
33179     init: function(grid){
33180         Roo.grid.GridView.superclass.init.call(this, grid);
33181
33182         this.bind(grid.dataSource, grid.colModel);
33183
33184         grid.on("headerclick", this.handleHeaderClick, this);
33185
33186         if(grid.trackMouseOver){
33187             grid.on("mouseover", this.onRowOver, this);
33188             grid.on("mouseout", this.onRowOut, this);
33189         }
33190         grid.cancelTextSelection = function(){};
33191         this.gridId = grid.id;
33192
33193         var tpls = this.templates || {};
33194
33195         if(!tpls.master){
33196             tpls.master = new Roo.Template(
33197                '<div class="x-grid" hidefocus="true">',
33198                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33199                   '<div class="x-grid-topbar"></div>',
33200                   '<div class="x-grid-scroller"><div></div></div>',
33201                   '<div class="x-grid-locked">',
33202                       '<div class="x-grid-header">{lockedHeader}</div>',
33203                       '<div class="x-grid-body">{lockedBody}</div>',
33204                   "</div>",
33205                   '<div class="x-grid-viewport">',
33206                       '<div class="x-grid-header">{header}</div>',
33207                       '<div class="x-grid-body">{body}</div>',
33208                   "</div>",
33209                   '<div class="x-grid-bottombar"></div>',
33210                  
33211                   '<div class="x-grid-resize-proxy">&#160;</div>',
33212                "</div>"
33213             );
33214             tpls.master.disableformats = true;
33215         }
33216
33217         if(!tpls.header){
33218             tpls.header = new Roo.Template(
33219                '<table border="0" cellspacing="0" cellpadding="0">',
33220                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33221                "</table>{splits}"
33222             );
33223             tpls.header.disableformats = true;
33224         }
33225         tpls.header.compile();
33226
33227         if(!tpls.hcell){
33228             tpls.hcell = new Roo.Template(
33229                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33230                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33231                 "</div></td>"
33232              );
33233              tpls.hcell.disableFormats = true;
33234         }
33235         tpls.hcell.compile();
33236
33237         if(!tpls.hsplit){
33238             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33239             tpls.hsplit.disableFormats = true;
33240         }
33241         tpls.hsplit.compile();
33242
33243         if(!tpls.body){
33244             tpls.body = new Roo.Template(
33245                '<table border="0" cellspacing="0" cellpadding="0">',
33246                "<tbody>{rows}</tbody>",
33247                "</table>"
33248             );
33249             tpls.body.disableFormats = true;
33250         }
33251         tpls.body.compile();
33252
33253         if(!tpls.row){
33254             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33255             tpls.row.disableFormats = true;
33256         }
33257         tpls.row.compile();
33258
33259         if(!tpls.cell){
33260             tpls.cell = new Roo.Template(
33261                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33262                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33263                 "</td>"
33264             );
33265             tpls.cell.disableFormats = true;
33266         }
33267         tpls.cell.compile();
33268
33269         this.templates = tpls;
33270     },
33271
33272     // remap these for backwards compat
33273     onColWidthChange : function(){
33274         this.updateColumns.apply(this, arguments);
33275     },
33276     onHeaderChange : function(){
33277         this.updateHeaders.apply(this, arguments);
33278     }, 
33279     onHiddenChange : function(){
33280         this.handleHiddenChange.apply(this, arguments);
33281     },
33282     onColumnMove : function(){
33283         this.handleColumnMove.apply(this, arguments);
33284     },
33285     onColumnLock : function(){
33286         this.handleLockChange.apply(this, arguments);
33287     },
33288
33289     onDataChange : function(){
33290         this.refresh();
33291         this.updateHeaderSortState();
33292     },
33293
33294     onClear : function(){
33295         this.refresh();
33296     },
33297
33298     onUpdate : function(ds, record){
33299         this.refreshRow(record);
33300     },
33301
33302     refreshRow : function(record){
33303         var ds = this.ds, index;
33304         if(typeof record == 'number'){
33305             index = record;
33306             record = ds.getAt(index);
33307         }else{
33308             index = ds.indexOf(record);
33309         }
33310         this.insertRows(ds, index, index, true);
33311         this.onRemove(ds, record, index+1, true);
33312         this.syncRowHeights(index, index);
33313         this.layout();
33314         this.fireEvent("rowupdated", this, index, record);
33315     },
33316
33317     onAdd : function(ds, records, index){
33318         this.insertRows(ds, index, index + (records.length-1));
33319     },
33320
33321     onRemove : function(ds, record, index, isUpdate){
33322         if(isUpdate !== true){
33323             this.fireEvent("beforerowremoved", this, index, record);
33324         }
33325         var bt = this.getBodyTable(), lt = this.getLockedTable();
33326         if(bt.rows[index]){
33327             bt.firstChild.removeChild(bt.rows[index]);
33328         }
33329         if(lt.rows[index]){
33330             lt.firstChild.removeChild(lt.rows[index]);
33331         }
33332         if(isUpdate !== true){
33333             this.stripeRows(index);
33334             this.syncRowHeights(index, index);
33335             this.layout();
33336             this.fireEvent("rowremoved", this, index, record);
33337         }
33338     },
33339
33340     onLoad : function(){
33341         this.scrollToTop();
33342     },
33343
33344     /**
33345      * Scrolls the grid to the top
33346      */
33347     scrollToTop : function(){
33348         if(this.scroller){
33349             this.scroller.dom.scrollTop = 0;
33350             this.syncScroll();
33351         }
33352     },
33353
33354     /**
33355      * Gets a panel in the header of the grid that can be used for toolbars etc.
33356      * After modifying the contents of this panel a call to grid.autoSize() may be
33357      * required to register any changes in size.
33358      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33359      * @return Roo.Element
33360      */
33361     getHeaderPanel : function(doShow){
33362         if(doShow){
33363             this.headerPanel.show();
33364         }
33365         return this.headerPanel;
33366     },
33367
33368     /**
33369      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33370      * After modifying the contents of this panel a call to grid.autoSize() may be
33371      * required to register any changes in size.
33372      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33373      * @return Roo.Element
33374      */
33375     getFooterPanel : function(doShow){
33376         if(doShow){
33377             this.footerPanel.show();
33378         }
33379         return this.footerPanel;
33380     },
33381
33382     initElements : function(){
33383         var E = Roo.Element;
33384         var el = this.grid.getGridEl().dom.firstChild;
33385         var cs = el.childNodes;
33386
33387         this.el = new E(el);
33388         
33389          this.focusEl = new E(el.firstChild);
33390         this.focusEl.swallowEvent("click", true);
33391         
33392         this.headerPanel = new E(cs[1]);
33393         this.headerPanel.enableDisplayMode("block");
33394
33395         this.scroller = new E(cs[2]);
33396         this.scrollSizer = new E(this.scroller.dom.firstChild);
33397
33398         this.lockedWrap = new E(cs[3]);
33399         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33400         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33401
33402         this.mainWrap = new E(cs[4]);
33403         this.mainHd = new E(this.mainWrap.dom.firstChild);
33404         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33405
33406         this.footerPanel = new E(cs[5]);
33407         this.footerPanel.enableDisplayMode("block");
33408
33409         this.resizeProxy = new E(cs[6]);
33410
33411         this.headerSelector = String.format(
33412            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33413            this.lockedHd.id, this.mainHd.id
33414         );
33415
33416         this.splitterSelector = String.format(
33417            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33418            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33419         );
33420     },
33421     idToCssName : function(s)
33422     {
33423         return s.replace(/[^a-z0-9]+/ig, '-');
33424     },
33425
33426     getHeaderCell : function(index){
33427         return Roo.DomQuery.select(this.headerSelector)[index];
33428     },
33429
33430     getHeaderCellMeasure : function(index){
33431         return this.getHeaderCell(index).firstChild;
33432     },
33433
33434     getHeaderCellText : function(index){
33435         return this.getHeaderCell(index).firstChild.firstChild;
33436     },
33437
33438     getLockedTable : function(){
33439         return this.lockedBody.dom.firstChild;
33440     },
33441
33442     getBodyTable : function(){
33443         return this.mainBody.dom.firstChild;
33444     },
33445
33446     getLockedRow : function(index){
33447         return this.getLockedTable().rows[index];
33448     },
33449
33450     getRow : function(index){
33451         return this.getBodyTable().rows[index];
33452     },
33453
33454     getRowComposite : function(index){
33455         if(!this.rowEl){
33456             this.rowEl = new Roo.CompositeElementLite();
33457         }
33458         var els = [], lrow, mrow;
33459         if(lrow = this.getLockedRow(index)){
33460             els.push(lrow);
33461         }
33462         if(mrow = this.getRow(index)){
33463             els.push(mrow);
33464         }
33465         this.rowEl.elements = els;
33466         return this.rowEl;
33467     },
33468
33469     getCell : function(rowIndex, colIndex){
33470         var locked = this.cm.getLockedCount();
33471         var source;
33472         if(colIndex < locked){
33473             source = this.lockedBody.dom.firstChild;
33474         }else{
33475             source = this.mainBody.dom.firstChild;
33476             colIndex -= locked;
33477         }
33478         return source.rows[rowIndex].childNodes[colIndex];
33479     },
33480
33481     getCellText : function(rowIndex, colIndex){
33482         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33483     },
33484
33485     getCellBox : function(cell){
33486         var b = this.fly(cell).getBox();
33487         if(Roo.isOpera){ // opera fails to report the Y
33488             b.y = cell.offsetTop + this.mainBody.getY();
33489         }
33490         return b;
33491     },
33492
33493     getCellIndex : function(cell){
33494         var id = String(cell.className).match(this.cellRE);
33495         if(id){
33496             return parseInt(id[1], 10);
33497         }
33498         return 0;
33499     },
33500
33501     findHeaderIndex : function(n){
33502         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33503         return r ? this.getCellIndex(r) : false;
33504     },
33505
33506     findHeaderCell : function(n){
33507         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33508         return r ? r : false;
33509     },
33510
33511     findRowIndex : function(n){
33512         if(!n){
33513             return false;
33514         }
33515         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33516         return r ? r.rowIndex : false;
33517     },
33518
33519     findCellIndex : function(node){
33520         var stop = this.el.dom;
33521         while(node && node != stop){
33522             if(this.findRE.test(node.className)){
33523                 return this.getCellIndex(node);
33524             }
33525             node = node.parentNode;
33526         }
33527         return false;
33528     },
33529
33530     getColumnId : function(index){
33531         return this.cm.getColumnId(index);
33532     },
33533
33534     getSplitters : function()
33535     {
33536         if(this.splitterSelector){
33537            return Roo.DomQuery.select(this.splitterSelector);
33538         }else{
33539             return null;
33540       }
33541     },
33542
33543     getSplitter : function(index){
33544         return this.getSplitters()[index];
33545     },
33546
33547     onRowOver : function(e, t){
33548         var row;
33549         if((row = this.findRowIndex(t)) !== false){
33550             this.getRowComposite(row).addClass("x-grid-row-over");
33551         }
33552     },
33553
33554     onRowOut : function(e, t){
33555         var row;
33556         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33557             this.getRowComposite(row).removeClass("x-grid-row-over");
33558         }
33559     },
33560
33561     renderHeaders : function(){
33562         var cm = this.cm;
33563         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33564         var cb = [], lb = [], sb = [], lsb = [], p = {};
33565         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33566             p.cellId = "x-grid-hd-0-" + i;
33567             p.splitId = "x-grid-csplit-0-" + i;
33568             p.id = cm.getColumnId(i);
33569             p.title = cm.getColumnTooltip(i) || "";
33570             p.value = cm.getColumnHeader(i) || "";
33571             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33572             if(!cm.isLocked(i)){
33573                 cb[cb.length] = ct.apply(p);
33574                 sb[sb.length] = st.apply(p);
33575             }else{
33576                 lb[lb.length] = ct.apply(p);
33577                 lsb[lsb.length] = st.apply(p);
33578             }
33579         }
33580         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33581                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33582     },
33583
33584     updateHeaders : function(){
33585         var html = this.renderHeaders();
33586         this.lockedHd.update(html[0]);
33587         this.mainHd.update(html[1]);
33588     },
33589
33590     /**
33591      * Focuses the specified row.
33592      * @param {Number} row The row index
33593      */
33594     focusRow : function(row)
33595     {
33596         //Roo.log('GridView.focusRow');
33597         var x = this.scroller.dom.scrollLeft;
33598         this.focusCell(row, 0, false);
33599         this.scroller.dom.scrollLeft = x;
33600     },
33601
33602     /**
33603      * Focuses the specified cell.
33604      * @param {Number} row The row index
33605      * @param {Number} col The column index
33606      * @param {Boolean} hscroll false to disable horizontal scrolling
33607      */
33608     focusCell : function(row, col, hscroll)
33609     {
33610         //Roo.log('GridView.focusCell');
33611         var el = this.ensureVisible(row, col, hscroll);
33612         this.focusEl.alignTo(el, "tl-tl");
33613         if(Roo.isGecko){
33614             this.focusEl.focus();
33615         }else{
33616             this.focusEl.focus.defer(1, this.focusEl);
33617         }
33618     },
33619
33620     /**
33621      * Scrolls the specified cell into view
33622      * @param {Number} row The row index
33623      * @param {Number} col The column index
33624      * @param {Boolean} hscroll false to disable horizontal scrolling
33625      */
33626     ensureVisible : function(row, col, hscroll)
33627     {
33628         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33629         //return null; //disable for testing.
33630         if(typeof row != "number"){
33631             row = row.rowIndex;
33632         }
33633         if(row < 0 && row >= this.ds.getCount()){
33634             return  null;
33635         }
33636         col = (col !== undefined ? col : 0);
33637         var cm = this.grid.colModel;
33638         while(cm.isHidden(col)){
33639             col++;
33640         }
33641
33642         var el = this.getCell(row, col);
33643         if(!el){
33644             return null;
33645         }
33646         var c = this.scroller.dom;
33647
33648         var ctop = parseInt(el.offsetTop, 10);
33649         var cleft = parseInt(el.offsetLeft, 10);
33650         var cbot = ctop + el.offsetHeight;
33651         var cright = cleft + el.offsetWidth;
33652         
33653         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33654         var stop = parseInt(c.scrollTop, 10);
33655         var sleft = parseInt(c.scrollLeft, 10);
33656         var sbot = stop + ch;
33657         var sright = sleft + c.clientWidth;
33658         /*
33659         Roo.log('GridView.ensureVisible:' +
33660                 ' ctop:' + ctop +
33661                 ' c.clientHeight:' + c.clientHeight +
33662                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33663                 ' stop:' + stop +
33664                 ' cbot:' + cbot +
33665                 ' sbot:' + sbot +
33666                 ' ch:' + ch  
33667                 );
33668         */
33669         if(ctop < stop){
33670              c.scrollTop = ctop;
33671             //Roo.log("set scrolltop to ctop DISABLE?");
33672         }else if(cbot > sbot){
33673             //Roo.log("set scrolltop to cbot-ch");
33674             c.scrollTop = cbot-ch;
33675         }
33676         
33677         if(hscroll !== false){
33678             if(cleft < sleft){
33679                 c.scrollLeft = cleft;
33680             }else if(cright > sright){
33681                 c.scrollLeft = cright-c.clientWidth;
33682             }
33683         }
33684          
33685         return el;
33686     },
33687
33688     updateColumns : function(){
33689         this.grid.stopEditing();
33690         var cm = this.grid.colModel, colIds = this.getColumnIds();
33691         //var totalWidth = cm.getTotalWidth();
33692         var pos = 0;
33693         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33694             //if(cm.isHidden(i)) continue;
33695             var w = cm.getColumnWidth(i);
33696             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33697             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33698         }
33699         this.updateSplitters();
33700     },
33701
33702     generateRules : function(cm){
33703         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33704         Roo.util.CSS.removeStyleSheet(rulesId);
33705         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33706             var cid = cm.getColumnId(i);
33707             var align = '';
33708             if(cm.config[i].align){
33709                 align = 'text-align:'+cm.config[i].align+';';
33710             }
33711             var hidden = '';
33712             if(cm.isHidden(i)){
33713                 hidden = 'display:none;';
33714             }
33715             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33716             ruleBuf.push(
33717                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33718                     this.hdSelector, cid, " {\n", align, width, "}\n",
33719                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33720                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33721         }
33722         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33723     },
33724
33725     updateSplitters : function(){
33726         var cm = this.cm, s = this.getSplitters();
33727         if(s){ // splitters not created yet
33728             var pos = 0, locked = true;
33729             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33730                 if(cm.isHidden(i)) continue;
33731                 var w = cm.getColumnWidth(i); // make sure it's a number
33732                 if(!cm.isLocked(i) && locked){
33733                     pos = 0;
33734                     locked = false;
33735                 }
33736                 pos += w;
33737                 s[i].style.left = (pos-this.splitOffset) + "px";
33738             }
33739         }
33740     },
33741
33742     handleHiddenChange : function(colModel, colIndex, hidden){
33743         if(hidden){
33744             this.hideColumn(colIndex);
33745         }else{
33746             this.unhideColumn(colIndex);
33747         }
33748     },
33749
33750     hideColumn : function(colIndex){
33751         var cid = this.getColumnId(colIndex);
33752         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33753         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33754         if(Roo.isSafari){
33755             this.updateHeaders();
33756         }
33757         this.updateSplitters();
33758         this.layout();
33759     },
33760
33761     unhideColumn : function(colIndex){
33762         var cid = this.getColumnId(colIndex);
33763         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33764         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33765
33766         if(Roo.isSafari){
33767             this.updateHeaders();
33768         }
33769         this.updateSplitters();
33770         this.layout();
33771     },
33772
33773     insertRows : function(dm, firstRow, lastRow, isUpdate){
33774         if(firstRow == 0 && lastRow == dm.getCount()-1){
33775             this.refresh();
33776         }else{
33777             if(!isUpdate){
33778                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33779             }
33780             var s = this.getScrollState();
33781             var markup = this.renderRows(firstRow, lastRow);
33782             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33783             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33784             this.restoreScroll(s);
33785             if(!isUpdate){
33786                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33787                 this.syncRowHeights(firstRow, lastRow);
33788                 this.stripeRows(firstRow);
33789                 this.layout();
33790             }
33791         }
33792     },
33793
33794     bufferRows : function(markup, target, index){
33795         var before = null, trows = target.rows, tbody = target.tBodies[0];
33796         if(index < trows.length){
33797             before = trows[index];
33798         }
33799         var b = document.createElement("div");
33800         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33801         var rows = b.firstChild.rows;
33802         for(var i = 0, len = rows.length; i < len; i++){
33803             if(before){
33804                 tbody.insertBefore(rows[0], before);
33805             }else{
33806                 tbody.appendChild(rows[0]);
33807             }
33808         }
33809         b.innerHTML = "";
33810         b = null;
33811     },
33812
33813     deleteRows : function(dm, firstRow, lastRow){
33814         if(dm.getRowCount()<1){
33815             this.fireEvent("beforerefresh", this);
33816             this.mainBody.update("");
33817             this.lockedBody.update("");
33818             this.fireEvent("refresh", this);
33819         }else{
33820             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33821             var bt = this.getBodyTable();
33822             var tbody = bt.firstChild;
33823             var rows = bt.rows;
33824             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33825                 tbody.removeChild(rows[firstRow]);
33826             }
33827             this.stripeRows(firstRow);
33828             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33829         }
33830     },
33831
33832     updateRows : function(dataSource, firstRow, lastRow){
33833         var s = this.getScrollState();
33834         this.refresh();
33835         this.restoreScroll(s);
33836     },
33837
33838     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33839         if(!noRefresh){
33840            this.refresh();
33841         }
33842         this.updateHeaderSortState();
33843     },
33844
33845     getScrollState : function(){
33846         
33847         var sb = this.scroller.dom;
33848         return {left: sb.scrollLeft, top: sb.scrollTop};
33849     },
33850
33851     stripeRows : function(startRow){
33852         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33853             return;
33854         }
33855         startRow = startRow || 0;
33856         var rows = this.getBodyTable().rows;
33857         var lrows = this.getLockedTable().rows;
33858         var cls = ' x-grid-row-alt ';
33859         for(var i = startRow, len = rows.length; i < len; i++){
33860             var row = rows[i], lrow = lrows[i];
33861             var isAlt = ((i+1) % 2 == 0);
33862             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33863             if(isAlt == hasAlt){
33864                 continue;
33865             }
33866             if(isAlt){
33867                 row.className += " x-grid-row-alt";
33868             }else{
33869                 row.className = row.className.replace("x-grid-row-alt", "");
33870             }
33871             if(lrow){
33872                 lrow.className = row.className;
33873             }
33874         }
33875     },
33876
33877     restoreScroll : function(state){
33878         //Roo.log('GridView.restoreScroll');
33879         var sb = this.scroller.dom;
33880         sb.scrollLeft = state.left;
33881         sb.scrollTop = state.top;
33882         this.syncScroll();
33883     },
33884
33885     syncScroll : function(){
33886         //Roo.log('GridView.syncScroll');
33887         var sb = this.scroller.dom;
33888         var sh = this.mainHd.dom;
33889         var bs = this.mainBody.dom;
33890         var lv = this.lockedBody.dom;
33891         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33892         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33893     },
33894
33895     handleScroll : function(e){
33896         this.syncScroll();
33897         var sb = this.scroller.dom;
33898         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33899         e.stopEvent();
33900     },
33901
33902     handleWheel : function(e){
33903         var d = e.getWheelDelta();
33904         this.scroller.dom.scrollTop -= d*22;
33905         // set this here to prevent jumpy scrolling on large tables
33906         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33907         e.stopEvent();
33908     },
33909
33910     renderRows : function(startRow, endRow){
33911         // pull in all the crap needed to render rows
33912         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33913         var colCount = cm.getColumnCount();
33914
33915         if(ds.getCount() < 1){
33916             return ["", ""];
33917         }
33918
33919         // build a map for all the columns
33920         var cs = [];
33921         for(var i = 0; i < colCount; i++){
33922             var name = cm.getDataIndex(i);
33923             cs[i] = {
33924                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33925                 renderer : cm.getRenderer(i),
33926                 id : cm.getColumnId(i),
33927                 locked : cm.isLocked(i)
33928             };
33929         }
33930
33931         startRow = startRow || 0;
33932         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33933
33934         // records to render
33935         var rs = ds.getRange(startRow, endRow);
33936
33937         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33938     },
33939
33940     // As much as I hate to duplicate code, this was branched because FireFox really hates
33941     // [].join("") on strings. The performance difference was substantial enough to
33942     // branch this function
33943     doRender : Roo.isGecko ?
33944             function(cs, rs, ds, startRow, colCount, stripe){
33945                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33946                 // buffers
33947                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33948                 
33949                 var hasListener = this.grid.hasListener('rowclass');
33950                 var rowcfg = {};
33951                 for(var j = 0, len = rs.length; j < len; j++){
33952                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33953                     for(var i = 0; i < colCount; i++){
33954                         c = cs[i];
33955                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33956                         p.id = c.id;
33957                         p.css = p.attr = "";
33958                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33959                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33960                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33961                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33962                         }
33963                         var markup = ct.apply(p);
33964                         if(!c.locked){
33965                             cb+= markup;
33966                         }else{
33967                             lcb+= markup;
33968                         }
33969                     }
33970                     var alt = [];
33971                     if(stripe && ((rowIndex+1) % 2 == 0)){
33972                         alt.push("x-grid-row-alt")
33973                     }
33974                     if(r.dirty){
33975                         alt.push(  " x-grid-dirty-row");
33976                     }
33977                     rp.cells = lcb;
33978                     if(this.getRowClass){
33979                         alt.push(this.getRowClass(r, rowIndex));
33980                     }
33981                     if (hasListener) {
33982                         rowcfg = {
33983                              
33984                             record: r,
33985                             rowIndex : rowIndex,
33986                             rowClass : ''
33987                         }
33988                         this.grid.fireEvent('rowclass', this, rowcfg);
33989                         alt.push(rowcfg.rowClass);
33990                     }
33991                     rp.alt = alt.join(" ");
33992                     lbuf+= rt.apply(rp);
33993                     rp.cells = cb;
33994                     buf+=  rt.apply(rp);
33995                 }
33996                 return [lbuf, buf];
33997             } :
33998             function(cs, rs, ds, startRow, colCount, stripe){
33999                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34000                 // buffers
34001                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34002                 var hasListener = this.grid.hasListener('rowclass');
34003                 var rowcfg = {};
34004                 for(var j = 0, len = rs.length; j < len; j++){
34005                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34006                     for(var i = 0; i < colCount; i++){
34007                         c = cs[i];
34008                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34009                         p.id = c.id;
34010                         p.css = p.attr = "";
34011                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34012                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34013                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34014                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34015                         }
34016                         var markup = ct.apply(p);
34017                         if(!c.locked){
34018                             cb[cb.length] = markup;
34019                         }else{
34020                             lcb[lcb.length] = markup;
34021                         }
34022                     }
34023                     var alt = [];
34024                     if(stripe && ((rowIndex+1) % 2 == 0)){
34025                         alt.push( "x-grid-row-alt");
34026                     }
34027                     if(r.dirty){
34028                         alt.push(" x-grid-dirty-row");
34029                     }
34030                     rp.cells = lcb;
34031                     if(this.getRowClass){
34032                         alt.push( this.getRowClass(r, rowIndex));
34033                     }
34034                     if (hasListener) {
34035                         rowcfg = {
34036                              
34037                             record: r,
34038                             rowIndex : rowIndex,
34039                             rowClass : ''
34040                         }
34041                         this.grid.fireEvent('rowclass', this, rowcfg);
34042                         alt.push(rowcfg.rowClass);
34043                     }
34044                     rp.alt = alt.join(" ");
34045                     rp.cells = lcb.join("");
34046                     lbuf[lbuf.length] = rt.apply(rp);
34047                     rp.cells = cb.join("");
34048                     buf[buf.length] =  rt.apply(rp);
34049                 }
34050                 return [lbuf.join(""), buf.join("")];
34051             },
34052
34053     renderBody : function(){
34054         var markup = this.renderRows();
34055         var bt = this.templates.body;
34056         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34057     },
34058
34059     /**
34060      * Refreshes the grid
34061      * @param {Boolean} headersToo
34062      */
34063     refresh : function(headersToo){
34064         this.fireEvent("beforerefresh", this);
34065         this.grid.stopEditing();
34066         var result = this.renderBody();
34067         this.lockedBody.update(result[0]);
34068         this.mainBody.update(result[1]);
34069         if(headersToo === true){
34070             this.updateHeaders();
34071             this.updateColumns();
34072             this.updateSplitters();
34073             this.updateHeaderSortState();
34074         }
34075         this.syncRowHeights();
34076         this.layout();
34077         this.fireEvent("refresh", this);
34078     },
34079
34080     handleColumnMove : function(cm, oldIndex, newIndex){
34081         this.indexMap = null;
34082         var s = this.getScrollState();
34083         this.refresh(true);
34084         this.restoreScroll(s);
34085         this.afterMove(newIndex);
34086     },
34087
34088     afterMove : function(colIndex){
34089         if(this.enableMoveAnim && Roo.enableFx){
34090             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34091         }
34092         // if multisort - fix sortOrder, and reload..
34093         if (this.grid.dataSource.multiSort) {
34094             // the we can call sort again..
34095             var dm = this.grid.dataSource;
34096             var cm = this.grid.colModel;
34097             var so = [];
34098             for(var i = 0; i < cm.config.length; i++ ) {
34099                 
34100                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34101                     continue; // dont' bother, it's not in sort list or being set.
34102                 }
34103                 
34104                 so.push(cm.config[i].dataIndex);
34105             };
34106             dm.sortOrder = so;
34107             dm.load(dm.lastOptions);
34108             
34109             
34110         }
34111         
34112     },
34113
34114     updateCell : function(dm, rowIndex, dataIndex){
34115         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34116         if(typeof colIndex == "undefined"){ // not present in grid
34117             return;
34118         }
34119         var cm = this.grid.colModel;
34120         var cell = this.getCell(rowIndex, colIndex);
34121         var cellText = this.getCellText(rowIndex, colIndex);
34122
34123         var p = {
34124             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34125             id : cm.getColumnId(colIndex),
34126             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34127         };
34128         var renderer = cm.getRenderer(colIndex);
34129         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34130         if(typeof val == "undefined" || val === "") val = "&#160;";
34131         cellText.innerHTML = val;
34132         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34133         this.syncRowHeights(rowIndex, rowIndex);
34134     },
34135
34136     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34137         var maxWidth = 0;
34138         if(this.grid.autoSizeHeaders){
34139             var h = this.getHeaderCellMeasure(colIndex);
34140             maxWidth = Math.max(maxWidth, h.scrollWidth);
34141         }
34142         var tb, index;
34143         if(this.cm.isLocked(colIndex)){
34144             tb = this.getLockedTable();
34145             index = colIndex;
34146         }else{
34147             tb = this.getBodyTable();
34148             index = colIndex - this.cm.getLockedCount();
34149         }
34150         if(tb && tb.rows){
34151             var rows = tb.rows;
34152             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34153             for(var i = 0; i < stopIndex; i++){
34154                 var cell = rows[i].childNodes[index].firstChild;
34155                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34156             }
34157         }
34158         return maxWidth + /*margin for error in IE*/ 5;
34159     },
34160     /**
34161      * Autofit a column to its content.
34162      * @param {Number} colIndex
34163      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34164      */
34165      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34166          if(this.cm.isHidden(colIndex)){
34167              return; // can't calc a hidden column
34168          }
34169         if(forceMinSize){
34170             var cid = this.cm.getColumnId(colIndex);
34171             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34172            if(this.grid.autoSizeHeaders){
34173                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34174            }
34175         }
34176         var newWidth = this.calcColumnWidth(colIndex);
34177         this.cm.setColumnWidth(colIndex,
34178             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34179         if(!suppressEvent){
34180             this.grid.fireEvent("columnresize", colIndex, newWidth);
34181         }
34182     },
34183
34184     /**
34185      * Autofits all columns to their content and then expands to fit any extra space in the grid
34186      */
34187      autoSizeColumns : function(){
34188         var cm = this.grid.colModel;
34189         var colCount = cm.getColumnCount();
34190         for(var i = 0; i < colCount; i++){
34191             this.autoSizeColumn(i, true, true);
34192         }
34193         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34194             this.fitColumns();
34195         }else{
34196             this.updateColumns();
34197             this.layout();
34198         }
34199     },
34200
34201     /**
34202      * Autofits all columns to the grid's width proportionate with their current size
34203      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34204      */
34205     fitColumns : function(reserveScrollSpace){
34206         var cm = this.grid.colModel;
34207         var colCount = cm.getColumnCount();
34208         var cols = [];
34209         var width = 0;
34210         var i, w;
34211         for (i = 0; i < colCount; i++){
34212             if(!cm.isHidden(i) && !cm.isFixed(i)){
34213                 w = cm.getColumnWidth(i);
34214                 cols.push(i);
34215                 cols.push(w);
34216                 width += w;
34217             }
34218         }
34219         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34220         if(reserveScrollSpace){
34221             avail -= 17;
34222         }
34223         var frac = (avail - cm.getTotalWidth())/width;
34224         while (cols.length){
34225             w = cols.pop();
34226             i = cols.pop();
34227             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34228         }
34229         this.updateColumns();
34230         this.layout();
34231     },
34232
34233     onRowSelect : function(rowIndex){
34234         var row = this.getRowComposite(rowIndex);
34235         row.addClass("x-grid-row-selected");
34236     },
34237
34238     onRowDeselect : function(rowIndex){
34239         var row = this.getRowComposite(rowIndex);
34240         row.removeClass("x-grid-row-selected");
34241     },
34242
34243     onCellSelect : function(row, col){
34244         var cell = this.getCell(row, col);
34245         if(cell){
34246             Roo.fly(cell).addClass("x-grid-cell-selected");
34247         }
34248     },
34249
34250     onCellDeselect : function(row, col){
34251         var cell = this.getCell(row, col);
34252         if(cell){
34253             Roo.fly(cell).removeClass("x-grid-cell-selected");
34254         }
34255     },
34256
34257     updateHeaderSortState : function(){
34258         
34259         // sort state can be single { field: xxx, direction : yyy}
34260         // or   { xxx=>ASC , yyy : DESC ..... }
34261         
34262         var mstate = {};
34263         if (!this.ds.multiSort) { 
34264             var state = this.ds.getSortState();
34265             if(!state){
34266                 return;
34267             }
34268             mstate[state.field] = state.direction;
34269             // FIXME... - this is not used here.. but might be elsewhere..
34270             this.sortState = state;
34271             
34272         } else {
34273             mstate = this.ds.sortToggle;
34274         }
34275         //remove existing sort classes..
34276         
34277         var sc = this.sortClasses;
34278         var hds = this.el.select(this.headerSelector).removeClass(sc);
34279         
34280         for(var f in mstate) {
34281         
34282             var sortColumn = this.cm.findColumnIndex(f);
34283             
34284             if(sortColumn != -1){
34285                 var sortDir = mstate[f];        
34286                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34287             }
34288         }
34289         
34290          
34291         
34292     },
34293
34294
34295     handleHeaderClick : function(g, index){
34296         if(this.headersDisabled){
34297             return;
34298         }
34299         var dm = g.dataSource, cm = g.colModel;
34300         if(!cm.isSortable(index)){
34301             return;
34302         }
34303         g.stopEditing();
34304         
34305         if (dm.multiSort) {
34306             // update the sortOrder
34307             var so = [];
34308             for(var i = 0; i < cm.config.length; i++ ) {
34309                 
34310                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34311                     continue; // dont' bother, it's not in sort list or being set.
34312                 }
34313                 
34314                 so.push(cm.config[i].dataIndex);
34315             };
34316             dm.sortOrder = so;
34317         }
34318         
34319         
34320         dm.sort(cm.getDataIndex(index));
34321     },
34322
34323
34324     destroy : function(){
34325         if(this.colMenu){
34326             this.colMenu.removeAll();
34327             Roo.menu.MenuMgr.unregister(this.colMenu);
34328             this.colMenu.getEl().remove();
34329             delete this.colMenu;
34330         }
34331         if(this.hmenu){
34332             this.hmenu.removeAll();
34333             Roo.menu.MenuMgr.unregister(this.hmenu);
34334             this.hmenu.getEl().remove();
34335             delete this.hmenu;
34336         }
34337         if(this.grid.enableColumnMove){
34338             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34339             if(dds){
34340                 for(var dd in dds){
34341                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34342                         var elid = dds[dd].dragElId;
34343                         dds[dd].unreg();
34344                         Roo.get(elid).remove();
34345                     } else if(dds[dd].config.isTarget){
34346                         dds[dd].proxyTop.remove();
34347                         dds[dd].proxyBottom.remove();
34348                         dds[dd].unreg();
34349                     }
34350                     if(Roo.dd.DDM.locationCache[dd]){
34351                         delete Roo.dd.DDM.locationCache[dd];
34352                     }
34353                 }
34354                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34355             }
34356         }
34357         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34358         this.bind(null, null);
34359         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34360     },
34361
34362     handleLockChange : function(){
34363         this.refresh(true);
34364     },
34365
34366     onDenyColumnLock : function(){
34367
34368     },
34369
34370     onDenyColumnHide : function(){
34371
34372     },
34373
34374     handleHdMenuClick : function(item){
34375         var index = this.hdCtxIndex;
34376         var cm = this.cm, ds = this.ds;
34377         switch(item.id){
34378             case "asc":
34379                 ds.sort(cm.getDataIndex(index), "ASC");
34380                 break;
34381             case "desc":
34382                 ds.sort(cm.getDataIndex(index), "DESC");
34383                 break;
34384             case "lock":
34385                 var lc = cm.getLockedCount();
34386                 if(cm.getColumnCount(true) <= lc+1){
34387                     this.onDenyColumnLock();
34388                     return;
34389                 }
34390                 if(lc != index){
34391                     cm.setLocked(index, true, true);
34392                     cm.moveColumn(index, lc);
34393                     this.grid.fireEvent("columnmove", index, lc);
34394                 }else{
34395                     cm.setLocked(index, true);
34396                 }
34397             break;
34398             case "unlock":
34399                 var lc = cm.getLockedCount();
34400                 if((lc-1) != index){
34401                     cm.setLocked(index, false, true);
34402                     cm.moveColumn(index, lc-1);
34403                     this.grid.fireEvent("columnmove", index, lc-1);
34404                 }else{
34405                     cm.setLocked(index, false);
34406                 }
34407             break;
34408             default:
34409                 index = cm.getIndexById(item.id.substr(4));
34410                 if(index != -1){
34411                     if(item.checked && cm.getColumnCount(true) <= 1){
34412                         this.onDenyColumnHide();
34413                         return false;
34414                     }
34415                     cm.setHidden(index, item.checked);
34416                 }
34417         }
34418         return true;
34419     },
34420
34421     beforeColMenuShow : function(){
34422         var cm = this.cm,  colCount = cm.getColumnCount();
34423         this.colMenu.removeAll();
34424         for(var i = 0; i < colCount; i++){
34425             this.colMenu.add(new Roo.menu.CheckItem({
34426                 id: "col-"+cm.getColumnId(i),
34427                 text: cm.getColumnHeader(i),
34428                 checked: !cm.isHidden(i),
34429                 hideOnClick:false
34430             }));
34431         }
34432     },
34433
34434     handleHdCtx : function(g, index, e){
34435         e.stopEvent();
34436         var hd = this.getHeaderCell(index);
34437         this.hdCtxIndex = index;
34438         var ms = this.hmenu.items, cm = this.cm;
34439         ms.get("asc").setDisabled(!cm.isSortable(index));
34440         ms.get("desc").setDisabled(!cm.isSortable(index));
34441         if(this.grid.enableColLock !== false){
34442             ms.get("lock").setDisabled(cm.isLocked(index));
34443             ms.get("unlock").setDisabled(!cm.isLocked(index));
34444         }
34445         this.hmenu.show(hd, "tl-bl");
34446     },
34447
34448     handleHdOver : function(e){
34449         var hd = this.findHeaderCell(e.getTarget());
34450         if(hd && !this.headersDisabled){
34451             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34452                this.fly(hd).addClass("x-grid-hd-over");
34453             }
34454         }
34455     },
34456
34457     handleHdOut : function(e){
34458         var hd = this.findHeaderCell(e.getTarget());
34459         if(hd){
34460             this.fly(hd).removeClass("x-grid-hd-over");
34461         }
34462     },
34463
34464     handleSplitDblClick : function(e, t){
34465         var i = this.getCellIndex(t);
34466         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34467             this.autoSizeColumn(i, true);
34468             this.layout();
34469         }
34470     },
34471
34472     render : function(){
34473
34474         var cm = this.cm;
34475         var colCount = cm.getColumnCount();
34476
34477         if(this.grid.monitorWindowResize === true){
34478             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34479         }
34480         var header = this.renderHeaders();
34481         var body = this.templates.body.apply({rows:""});
34482         var html = this.templates.master.apply({
34483             lockedBody: body,
34484             body: body,
34485             lockedHeader: header[0],
34486             header: header[1]
34487         });
34488
34489         //this.updateColumns();
34490
34491         this.grid.getGridEl().dom.innerHTML = html;
34492
34493         this.initElements();
34494         
34495         // a kludge to fix the random scolling effect in webkit
34496         this.el.on("scroll", function() {
34497             this.el.dom.scrollTop=0; // hopefully not recursive..
34498         },this);
34499
34500         this.scroller.on("scroll", this.handleScroll, this);
34501         this.lockedBody.on("mousewheel", this.handleWheel, this);
34502         this.mainBody.on("mousewheel", this.handleWheel, this);
34503
34504         this.mainHd.on("mouseover", this.handleHdOver, this);
34505         this.mainHd.on("mouseout", this.handleHdOut, this);
34506         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34507                 {delegate: "."+this.splitClass});
34508
34509         this.lockedHd.on("mouseover", this.handleHdOver, this);
34510         this.lockedHd.on("mouseout", this.handleHdOut, this);
34511         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34512                 {delegate: "."+this.splitClass});
34513
34514         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34515             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34516         }
34517
34518         this.updateSplitters();
34519
34520         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34521             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34522             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34523         }
34524
34525         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34526             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34527             this.hmenu.add(
34528                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34529                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34530             );
34531             if(this.grid.enableColLock !== false){
34532                 this.hmenu.add('-',
34533                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34534                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34535                 );
34536             }
34537             if(this.grid.enableColumnHide !== false){
34538
34539                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34540                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34541                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34542
34543                 this.hmenu.add('-',
34544                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34545                 );
34546             }
34547             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34548
34549             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34550         }
34551
34552         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34553             this.dd = new Roo.grid.GridDragZone(this.grid, {
34554                 ddGroup : this.grid.ddGroup || 'GridDD'
34555             });
34556         }
34557
34558         /*
34559         for(var i = 0; i < colCount; i++){
34560             if(cm.isHidden(i)){
34561                 this.hideColumn(i);
34562             }
34563             if(cm.config[i].align){
34564                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34565                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34566             }
34567         }*/
34568         
34569         this.updateHeaderSortState();
34570
34571         this.beforeInitialResize();
34572         this.layout(true);
34573
34574         // two part rendering gives faster view to the user
34575         this.renderPhase2.defer(1, this);
34576     },
34577
34578     renderPhase2 : function(){
34579         // render the rows now
34580         this.refresh();
34581         if(this.grid.autoSizeColumns){
34582             this.autoSizeColumns();
34583         }
34584     },
34585
34586     beforeInitialResize : function(){
34587
34588     },
34589
34590     onColumnSplitterMoved : function(i, w){
34591         this.userResized = true;
34592         var cm = this.grid.colModel;
34593         cm.setColumnWidth(i, w, true);
34594         var cid = cm.getColumnId(i);
34595         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34596         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34597         this.updateSplitters();
34598         this.layout();
34599         this.grid.fireEvent("columnresize", i, w);
34600     },
34601
34602     syncRowHeights : function(startIndex, endIndex){
34603         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34604             startIndex = startIndex || 0;
34605             var mrows = this.getBodyTable().rows;
34606             var lrows = this.getLockedTable().rows;
34607             var len = mrows.length-1;
34608             endIndex = Math.min(endIndex || len, len);
34609             for(var i = startIndex; i <= endIndex; i++){
34610                 var m = mrows[i], l = lrows[i];
34611                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34612                 m.style.height = l.style.height = h + "px";
34613             }
34614         }
34615     },
34616
34617     layout : function(initialRender, is2ndPass){
34618         var g = this.grid;
34619         var auto = g.autoHeight;
34620         var scrollOffset = 16;
34621         var c = g.getGridEl(), cm = this.cm,
34622                 expandCol = g.autoExpandColumn,
34623                 gv = this;
34624         //c.beginMeasure();
34625
34626         if(!c.dom.offsetWidth){ // display:none?
34627             if(initialRender){
34628                 this.lockedWrap.show();
34629                 this.mainWrap.show();
34630             }
34631             return;
34632         }
34633
34634         var hasLock = this.cm.isLocked(0);
34635
34636         var tbh = this.headerPanel.getHeight();
34637         var bbh = this.footerPanel.getHeight();
34638
34639         if(auto){
34640             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34641             var newHeight = ch + c.getBorderWidth("tb");
34642             if(g.maxHeight){
34643                 newHeight = Math.min(g.maxHeight, newHeight);
34644             }
34645             c.setHeight(newHeight);
34646         }
34647
34648         if(g.autoWidth){
34649             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34650         }
34651
34652         var s = this.scroller;
34653
34654         var csize = c.getSize(true);
34655
34656         this.el.setSize(csize.width, csize.height);
34657
34658         this.headerPanel.setWidth(csize.width);
34659         this.footerPanel.setWidth(csize.width);
34660
34661         var hdHeight = this.mainHd.getHeight();
34662         var vw = csize.width;
34663         var vh = csize.height - (tbh + bbh);
34664
34665         s.setSize(vw, vh);
34666
34667         var bt = this.getBodyTable();
34668         var ltWidth = hasLock ?
34669                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34670
34671         var scrollHeight = bt.offsetHeight;
34672         var scrollWidth = ltWidth + bt.offsetWidth;
34673         var vscroll = false, hscroll = false;
34674
34675         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34676
34677         var lw = this.lockedWrap, mw = this.mainWrap;
34678         var lb = this.lockedBody, mb = this.mainBody;
34679
34680         setTimeout(function(){
34681             var t = s.dom.offsetTop;
34682             var w = s.dom.clientWidth,
34683                 h = s.dom.clientHeight;
34684
34685             lw.setTop(t);
34686             lw.setSize(ltWidth, h);
34687
34688             mw.setLeftTop(ltWidth, t);
34689             mw.setSize(w-ltWidth, h);
34690
34691             lb.setHeight(h-hdHeight);
34692             mb.setHeight(h-hdHeight);
34693
34694             if(is2ndPass !== true && !gv.userResized && expandCol){
34695                 // high speed resize without full column calculation
34696                 
34697                 var ci = cm.getIndexById(expandCol);
34698                 if (ci < 0) {
34699                     ci = cm.findColumnIndex(expandCol);
34700                 }
34701                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34702                 var expandId = cm.getColumnId(ci);
34703                 var  tw = cm.getTotalWidth(false);
34704                 var currentWidth = cm.getColumnWidth(ci);
34705                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34706                 if(currentWidth != cw){
34707                     cm.setColumnWidth(ci, cw, true);
34708                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34709                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34710                     gv.updateSplitters();
34711                     gv.layout(false, true);
34712                 }
34713             }
34714
34715             if(initialRender){
34716                 lw.show();
34717                 mw.show();
34718             }
34719             //c.endMeasure();
34720         }, 10);
34721     },
34722
34723     onWindowResize : function(){
34724         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34725             return;
34726         }
34727         this.layout();
34728     },
34729
34730     appendFooter : function(parentEl){
34731         return null;
34732     },
34733
34734     sortAscText : "Sort Ascending",
34735     sortDescText : "Sort Descending",
34736     lockText : "Lock Column",
34737     unlockText : "Unlock Column",
34738     columnsText : "Columns"
34739 });
34740
34741
34742 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34743     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34744     this.proxy.el.addClass('x-grid3-col-dd');
34745 };
34746
34747 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34748     handleMouseDown : function(e){
34749
34750     },
34751
34752     callHandleMouseDown : function(e){
34753         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34754     }
34755 });
34756 /*
34757  * Based on:
34758  * Ext JS Library 1.1.1
34759  * Copyright(c) 2006-2007, Ext JS, LLC.
34760  *
34761  * Originally Released Under LGPL - original licence link has changed is not relivant.
34762  *
34763  * Fork - LGPL
34764  * <script type="text/javascript">
34765  */
34766  
34767 // private
34768 // This is a support class used internally by the Grid components
34769 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34770     this.grid = grid;
34771     this.view = grid.getView();
34772     this.proxy = this.view.resizeProxy;
34773     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34774         "gridSplitters" + this.grid.getGridEl().id, {
34775         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34776     });
34777     this.setHandleElId(Roo.id(hd));
34778     this.setOuterHandleElId(Roo.id(hd2));
34779     this.scroll = false;
34780 };
34781 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34782     fly: Roo.Element.fly,
34783
34784     b4StartDrag : function(x, y){
34785         this.view.headersDisabled = true;
34786         this.proxy.setHeight(this.view.mainWrap.getHeight());
34787         var w = this.cm.getColumnWidth(this.cellIndex);
34788         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34789         this.resetConstraints();
34790         this.setXConstraint(minw, 1000);
34791         this.setYConstraint(0, 0);
34792         this.minX = x - minw;
34793         this.maxX = x + 1000;
34794         this.startPos = x;
34795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34796     },
34797
34798
34799     handleMouseDown : function(e){
34800         ev = Roo.EventObject.setEvent(e);
34801         var t = this.fly(ev.getTarget());
34802         if(t.hasClass("x-grid-split")){
34803             this.cellIndex = this.view.getCellIndex(t.dom);
34804             this.split = t.dom;
34805             this.cm = this.grid.colModel;
34806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34808             }
34809         }
34810     },
34811
34812     endDrag : function(e){
34813         this.view.headersDisabled = false;
34814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34815         var diff = endX - this.startPos;
34816         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34817     },
34818
34819     autoOffset : function(){
34820         this.setDelta(0,0);
34821     }
34822 });/*
34823  * Based on:
34824  * Ext JS Library 1.1.1
34825  * Copyright(c) 2006-2007, Ext JS, LLC.
34826  *
34827  * Originally Released Under LGPL - original licence link has changed is not relivant.
34828  *
34829  * Fork - LGPL
34830  * <script type="text/javascript">
34831  */
34832  
34833 // private
34834 // This is a support class used internally by the Grid components
34835 Roo.grid.GridDragZone = function(grid, config){
34836     this.view = grid.getView();
34837     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34838     if(this.view.lockedBody){
34839         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34840         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34841     }
34842     this.scroll = false;
34843     this.grid = grid;
34844     this.ddel = document.createElement('div');
34845     this.ddel.className = 'x-grid-dd-wrap';
34846 };
34847
34848 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34849     ddGroup : "GridDD",
34850
34851     getDragData : function(e){
34852         var t = Roo.lib.Event.getTarget(e);
34853         var rowIndex = this.view.findRowIndex(t);
34854         if(rowIndex !== false){
34855             var sm = this.grid.selModel;
34856             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34857               //  sm.mouseDown(e, t);
34858             //}
34859             if (e.hasModifier()){
34860                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34861             }
34862             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34863         }
34864         return false;
34865     },
34866
34867     onInitDrag : function(e){
34868         var data = this.dragData;
34869         this.ddel.innerHTML = this.grid.getDragDropText();
34870         this.proxy.update(this.ddel);
34871         // fire start drag?
34872     },
34873
34874     afterRepair : function(){
34875         this.dragging = false;
34876     },
34877
34878     getRepairXY : function(e, data){
34879         return false;
34880     },
34881
34882     onEndDrag : function(data, e){
34883         // fire end drag?
34884     },
34885
34886     onValidDrop : function(dd, e, id){
34887         // fire drag drop?
34888         this.hideProxy();
34889     },
34890
34891     beforeInvalidDrop : function(e, id){
34892
34893     }
34894 });/*
34895  * Based on:
34896  * Ext JS Library 1.1.1
34897  * Copyright(c) 2006-2007, Ext JS, LLC.
34898  *
34899  * Originally Released Under LGPL - original licence link has changed is not relivant.
34900  *
34901  * Fork - LGPL
34902  * <script type="text/javascript">
34903  */
34904  
34905
34906 /**
34907  * @class Roo.grid.ColumnModel
34908  * @extends Roo.util.Observable
34909  * This is the default implementation of a ColumnModel used by the Grid. It defines
34910  * the columns in the grid.
34911  * <br>Usage:<br>
34912  <pre><code>
34913  var colModel = new Roo.grid.ColumnModel([
34914         {header: "Ticker", width: 60, sortable: true, locked: true},
34915         {header: "Company Name", width: 150, sortable: true},
34916         {header: "Market Cap.", width: 100, sortable: true},
34917         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34918         {header: "Employees", width: 100, sortable: true, resizable: false}
34919  ]);
34920  </code></pre>
34921  * <p>
34922  
34923  * The config options listed for this class are options which may appear in each
34924  * individual column definition.
34925  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34926  * @constructor
34927  * @param {Object} config An Array of column config objects. See this class's
34928  * config objects for details.
34929 */
34930 Roo.grid.ColumnModel = function(config){
34931         /**
34932      * The config passed into the constructor
34933      */
34934     this.config = config;
34935     this.lookup = {};
34936
34937     // if no id, create one
34938     // if the column does not have a dataIndex mapping,
34939     // map it to the order it is in the config
34940     for(var i = 0, len = config.length; i < len; i++){
34941         var c = config[i];
34942         if(typeof c.dataIndex == "undefined"){
34943             c.dataIndex = i;
34944         }
34945         if(typeof c.renderer == "string"){
34946             c.renderer = Roo.util.Format[c.renderer];
34947         }
34948         if(typeof c.id == "undefined"){
34949             c.id = Roo.id();
34950         }
34951         if(c.editor && c.editor.xtype){
34952             c.editor  = Roo.factory(c.editor, Roo.grid);
34953         }
34954         if(c.editor && c.editor.isFormField){
34955             c.editor = new Roo.grid.GridEditor(c.editor);
34956         }
34957         this.lookup[c.id] = c;
34958     }
34959
34960     /**
34961      * The width of columns which have no width specified (defaults to 100)
34962      * @type Number
34963      */
34964     this.defaultWidth = 100;
34965
34966     /**
34967      * Default sortable of columns which have no sortable specified (defaults to false)
34968      * @type Boolean
34969      */
34970     this.defaultSortable = false;
34971
34972     this.addEvents({
34973         /**
34974              * @event widthchange
34975              * Fires when the width of a column changes.
34976              * @param {ColumnModel} this
34977              * @param {Number} columnIndex The column index
34978              * @param {Number} newWidth The new width
34979              */
34980             "widthchange": true,
34981         /**
34982              * @event headerchange
34983              * Fires when the text of a header changes.
34984              * @param {ColumnModel} this
34985              * @param {Number} columnIndex The column index
34986              * @param {Number} newText The new header text
34987              */
34988             "headerchange": true,
34989         /**
34990              * @event hiddenchange
34991              * Fires when a column is hidden or "unhidden".
34992              * @param {ColumnModel} this
34993              * @param {Number} columnIndex The column index
34994              * @param {Boolean} hidden true if hidden, false otherwise
34995              */
34996             "hiddenchange": true,
34997             /**
34998          * @event columnmoved
34999          * Fires when a column is moved.
35000          * @param {ColumnModel} this
35001          * @param {Number} oldIndex
35002          * @param {Number} newIndex
35003          */
35004         "columnmoved" : true,
35005         /**
35006          * @event columlockchange
35007          * Fires when a column's locked state is changed
35008          * @param {ColumnModel} this
35009          * @param {Number} colIndex
35010          * @param {Boolean} locked true if locked
35011          */
35012         "columnlockchange" : true
35013     });
35014     Roo.grid.ColumnModel.superclass.constructor.call(this);
35015 };
35016 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35017     /**
35018      * @cfg {String} header The header text to display in the Grid view.
35019      */
35020     /**
35021      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35022      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35023      * specified, the column's index is used as an index into the Record's data Array.
35024      */
35025     /**
35026      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35027      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35028      */
35029     /**
35030      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35031      * Defaults to the value of the {@link #defaultSortable} property.
35032      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35033      */
35034     /**
35035      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35036      */
35037     /**
35038      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35039      */
35040     /**
35041      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35042      */
35043     /**
35044      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35045      */
35046     /**
35047      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35048      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35049      * default renderer uses the raw data value.
35050      */
35051        /**
35052      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35053      */
35054     /**
35055      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35056      */
35057
35058     /**
35059      * Returns the id of the column at the specified index.
35060      * @param {Number} index The column index
35061      * @return {String} the id
35062      */
35063     getColumnId : function(index){
35064         return this.config[index].id;
35065     },
35066
35067     /**
35068      * Returns the column for a specified id.
35069      * @param {String} id The column id
35070      * @return {Object} the column
35071      */
35072     getColumnById : function(id){
35073         return this.lookup[id];
35074     },
35075
35076     
35077     /**
35078      * Returns the column for a specified dataIndex.
35079      * @param {String} dataIndex The column dataIndex
35080      * @return {Object|Boolean} the column or false if not found
35081      */
35082     getColumnByDataIndex: function(dataIndex){
35083         var index = this.findColumnIndex(dataIndex);
35084         return index > -1 ? this.config[index] : false;
35085     },
35086     
35087     /**
35088      * Returns the index for a specified column id.
35089      * @param {String} id The column id
35090      * @return {Number} the index, or -1 if not found
35091      */
35092     getIndexById : function(id){
35093         for(var i = 0, len = this.config.length; i < len; i++){
35094             if(this.config[i].id == id){
35095                 return i;
35096             }
35097         }
35098         return -1;
35099     },
35100     
35101     /**
35102      * Returns the index for a specified column dataIndex.
35103      * @param {String} dataIndex The column dataIndex
35104      * @return {Number} the index, or -1 if not found
35105      */
35106     
35107     findColumnIndex : function(dataIndex){
35108         for(var i = 0, len = this.config.length; i < len; i++){
35109             if(this.config[i].dataIndex == dataIndex){
35110                 return i;
35111             }
35112         }
35113         return -1;
35114     },
35115     
35116     
35117     moveColumn : function(oldIndex, newIndex){
35118         var c = this.config[oldIndex];
35119         this.config.splice(oldIndex, 1);
35120         this.config.splice(newIndex, 0, c);
35121         this.dataMap = null;
35122         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35123     },
35124
35125     isLocked : function(colIndex){
35126         return this.config[colIndex].locked === true;
35127     },
35128
35129     setLocked : function(colIndex, value, suppressEvent){
35130         if(this.isLocked(colIndex) == value){
35131             return;
35132         }
35133         this.config[colIndex].locked = value;
35134         if(!suppressEvent){
35135             this.fireEvent("columnlockchange", this, colIndex, value);
35136         }
35137     },
35138
35139     getTotalLockedWidth : function(){
35140         var totalWidth = 0;
35141         for(var i = 0; i < this.config.length; i++){
35142             if(this.isLocked(i) && !this.isHidden(i)){
35143                 this.totalWidth += this.getColumnWidth(i);
35144             }
35145         }
35146         return totalWidth;
35147     },
35148
35149     getLockedCount : function(){
35150         for(var i = 0, len = this.config.length; i < len; i++){
35151             if(!this.isLocked(i)){
35152                 return i;
35153             }
35154         }
35155     },
35156
35157     /**
35158      * Returns the number of columns.
35159      * @return {Number}
35160      */
35161     getColumnCount : function(visibleOnly){
35162         if(visibleOnly === true){
35163             var c = 0;
35164             for(var i = 0, len = this.config.length; i < len; i++){
35165                 if(!this.isHidden(i)){
35166                     c++;
35167                 }
35168             }
35169             return c;
35170         }
35171         return this.config.length;
35172     },
35173
35174     /**
35175      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35176      * @param {Function} fn
35177      * @param {Object} scope (optional)
35178      * @return {Array} result
35179      */
35180     getColumnsBy : function(fn, scope){
35181         var r = [];
35182         for(var i = 0, len = this.config.length; i < len; i++){
35183             var c = this.config[i];
35184             if(fn.call(scope||this, c, i) === true){
35185                 r[r.length] = c;
35186             }
35187         }
35188         return r;
35189     },
35190
35191     /**
35192      * Returns true if the specified column is sortable.
35193      * @param {Number} col The column index
35194      * @return {Boolean}
35195      */
35196     isSortable : function(col){
35197         if(typeof this.config[col].sortable == "undefined"){
35198             return this.defaultSortable;
35199         }
35200         return this.config[col].sortable;
35201     },
35202
35203     /**
35204      * Returns the rendering (formatting) function defined for the column.
35205      * @param {Number} col The column index.
35206      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35207      */
35208     getRenderer : function(col){
35209         if(!this.config[col].renderer){
35210             return Roo.grid.ColumnModel.defaultRenderer;
35211         }
35212         return this.config[col].renderer;
35213     },
35214
35215     /**
35216      * Sets the rendering (formatting) function for a column.
35217      * @param {Number} col The column index
35218      * @param {Function} fn The function to use to process the cell's raw data
35219      * to return HTML markup for the grid view. The render function is called with
35220      * the following parameters:<ul>
35221      * <li>Data value.</li>
35222      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35223      * <li>css A CSS style string to apply to the table cell.</li>
35224      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35225      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35226      * <li>Row index</li>
35227      * <li>Column index</li>
35228      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35229      */
35230     setRenderer : function(col, fn){
35231         this.config[col].renderer = fn;
35232     },
35233
35234     /**
35235      * Returns the width for the specified column.
35236      * @param {Number} col The column index
35237      * @return {Number}
35238      */
35239     getColumnWidth : function(col){
35240         return this.config[col].width * 1 || this.defaultWidth;
35241     },
35242
35243     /**
35244      * Sets the width for a column.
35245      * @param {Number} col The column index
35246      * @param {Number} width The new width
35247      */
35248     setColumnWidth : function(col, width, suppressEvent){
35249         this.config[col].width = width;
35250         this.totalWidth = null;
35251         if(!suppressEvent){
35252              this.fireEvent("widthchange", this, col, width);
35253         }
35254     },
35255
35256     /**
35257      * Returns the total width of all columns.
35258      * @param {Boolean} includeHidden True to include hidden column widths
35259      * @return {Number}
35260      */
35261     getTotalWidth : function(includeHidden){
35262         if(!this.totalWidth){
35263             this.totalWidth = 0;
35264             for(var i = 0, len = this.config.length; i < len; i++){
35265                 if(includeHidden || !this.isHidden(i)){
35266                     this.totalWidth += this.getColumnWidth(i);
35267                 }
35268             }
35269         }
35270         return this.totalWidth;
35271     },
35272
35273     /**
35274      * Returns the header for the specified column.
35275      * @param {Number} col The column index
35276      * @return {String}
35277      */
35278     getColumnHeader : function(col){
35279         return this.config[col].header;
35280     },
35281
35282     /**
35283      * Sets the header for a column.
35284      * @param {Number} col The column index
35285      * @param {String} header The new header
35286      */
35287     setColumnHeader : function(col, header){
35288         this.config[col].header = header;
35289         this.fireEvent("headerchange", this, col, header);
35290     },
35291
35292     /**
35293      * Returns the tooltip for the specified column.
35294      * @param {Number} col The column index
35295      * @return {String}
35296      */
35297     getColumnTooltip : function(col){
35298             return this.config[col].tooltip;
35299     },
35300     /**
35301      * Sets the tooltip for a column.
35302      * @param {Number} col The column index
35303      * @param {String} tooltip The new tooltip
35304      */
35305     setColumnTooltip : function(col, tooltip){
35306             this.config[col].tooltip = tooltip;
35307     },
35308
35309     /**
35310      * Returns the dataIndex for the specified column.
35311      * @param {Number} col The column index
35312      * @return {Number}
35313      */
35314     getDataIndex : function(col){
35315         return this.config[col].dataIndex;
35316     },
35317
35318     /**
35319      * Sets the dataIndex for a column.
35320      * @param {Number} col The column index
35321      * @param {Number} dataIndex The new dataIndex
35322      */
35323     setDataIndex : function(col, dataIndex){
35324         this.config[col].dataIndex = dataIndex;
35325     },
35326
35327     
35328     
35329     /**
35330      * Returns true if the cell is editable.
35331      * @param {Number} colIndex The column index
35332      * @param {Number} rowIndex The row index
35333      * @return {Boolean}
35334      */
35335     isCellEditable : function(colIndex, rowIndex){
35336         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35337     },
35338
35339     /**
35340      * Returns the editor defined for the cell/column.
35341      * return false or null to disable editing.
35342      * @param {Number} colIndex The column index
35343      * @param {Number} rowIndex The row index
35344      * @return {Object}
35345      */
35346     getCellEditor : function(colIndex, rowIndex){
35347         return this.config[colIndex].editor;
35348     },
35349
35350     /**
35351      * Sets if a column is editable.
35352      * @param {Number} col The column index
35353      * @param {Boolean} editable True if the column is editable
35354      */
35355     setEditable : function(col, editable){
35356         this.config[col].editable = editable;
35357     },
35358
35359
35360     /**
35361      * Returns true if the column is hidden.
35362      * @param {Number} colIndex The column index
35363      * @return {Boolean}
35364      */
35365     isHidden : function(colIndex){
35366         return this.config[colIndex].hidden;
35367     },
35368
35369
35370     /**
35371      * Returns true if the column width cannot be changed
35372      */
35373     isFixed : function(colIndex){
35374         return this.config[colIndex].fixed;
35375     },
35376
35377     /**
35378      * Returns true if the column can be resized
35379      * @return {Boolean}
35380      */
35381     isResizable : function(colIndex){
35382         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35383     },
35384     /**
35385      * Sets if a column is hidden.
35386      * @param {Number} colIndex The column index
35387      * @param {Boolean} hidden True if the column is hidden
35388      */
35389     setHidden : function(colIndex, hidden){
35390         this.config[colIndex].hidden = hidden;
35391         this.totalWidth = null;
35392         this.fireEvent("hiddenchange", this, colIndex, hidden);
35393     },
35394
35395     /**
35396      * Sets the editor for a column.
35397      * @param {Number} col The column index
35398      * @param {Object} editor The editor object
35399      */
35400     setEditor : function(col, editor){
35401         this.config[col].editor = editor;
35402     }
35403 });
35404
35405 Roo.grid.ColumnModel.defaultRenderer = function(value){
35406         if(typeof value == "string" && value.length < 1){
35407             return "&#160;";
35408         }
35409         return value;
35410 };
35411
35412 // Alias for backwards compatibility
35413 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35414 /*
35415  * Based on:
35416  * Ext JS Library 1.1.1
35417  * Copyright(c) 2006-2007, Ext JS, LLC.
35418  *
35419  * Originally Released Under LGPL - original licence link has changed is not relivant.
35420  *
35421  * Fork - LGPL
35422  * <script type="text/javascript">
35423  */
35424
35425 /**
35426  * @class Roo.grid.AbstractSelectionModel
35427  * @extends Roo.util.Observable
35428  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35429  * implemented by descendant classes.  This class should not be directly instantiated.
35430  * @constructor
35431  */
35432 Roo.grid.AbstractSelectionModel = function(){
35433     this.locked = false;
35434     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35435 };
35436
35437 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35438     /** @ignore Called by the grid automatically. Do not call directly. */
35439     init : function(grid){
35440         this.grid = grid;
35441         this.initEvents();
35442     },
35443
35444     /**
35445      * Locks the selections.
35446      */
35447     lock : function(){
35448         this.locked = true;
35449     },
35450
35451     /**
35452      * Unlocks the selections.
35453      */
35454     unlock : function(){
35455         this.locked = false;
35456     },
35457
35458     /**
35459      * Returns true if the selections are locked.
35460      * @return {Boolean}
35461      */
35462     isLocked : function(){
35463         return this.locked;
35464     }
35465 });/*
35466  * Based on:
35467  * Ext JS Library 1.1.1
35468  * Copyright(c) 2006-2007, Ext JS, LLC.
35469  *
35470  * Originally Released Under LGPL - original licence link has changed is not relivant.
35471  *
35472  * Fork - LGPL
35473  * <script type="text/javascript">
35474  */
35475 /**
35476  * @extends Roo.grid.AbstractSelectionModel
35477  * @class Roo.grid.RowSelectionModel
35478  * The default SelectionModel used by {@link Roo.grid.Grid}.
35479  * It supports multiple selections and keyboard selection/navigation. 
35480  * @constructor
35481  * @param {Object} config
35482  */
35483 Roo.grid.RowSelectionModel = function(config){
35484     Roo.apply(this, config);
35485     this.selections = new Roo.util.MixedCollection(false, function(o){
35486         return o.id;
35487     });
35488
35489     this.last = false;
35490     this.lastActive = false;
35491
35492     this.addEvents({
35493         /**
35494              * @event selectionchange
35495              * Fires when the selection changes
35496              * @param {SelectionModel} this
35497              */
35498             "selectionchange" : true,
35499         /**
35500              * @event afterselectionchange
35501              * Fires after the selection changes (eg. by key press or clicking)
35502              * @param {SelectionModel} this
35503              */
35504             "afterselectionchange" : true,
35505         /**
35506              * @event beforerowselect
35507              * Fires when a row is selected being selected, return false to cancel.
35508              * @param {SelectionModel} this
35509              * @param {Number} rowIndex The selected index
35510              * @param {Boolean} keepExisting False if other selections will be cleared
35511              */
35512             "beforerowselect" : true,
35513         /**
35514              * @event rowselect
35515              * Fires when a row is selected.
35516              * @param {SelectionModel} this
35517              * @param {Number} rowIndex The selected index
35518              * @param {Roo.data.Record} r The record
35519              */
35520             "rowselect" : true,
35521         /**
35522              * @event rowdeselect
35523              * Fires when a row is deselected.
35524              * @param {SelectionModel} this
35525              * @param {Number} rowIndex The selected index
35526              */
35527         "rowdeselect" : true
35528     });
35529     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35530     this.locked = false;
35531 };
35532
35533 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35534     /**
35535      * @cfg {Boolean} singleSelect
35536      * True to allow selection of only one row at a time (defaults to false)
35537      */
35538     singleSelect : false,
35539
35540     // private
35541     initEvents : function(){
35542
35543         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35544             this.grid.on("mousedown", this.handleMouseDown, this);
35545         }else{ // allow click to work like normal
35546             this.grid.on("rowclick", this.handleDragableRowClick, this);
35547         }
35548
35549         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35550             "up" : function(e){
35551                 if(!e.shiftKey){
35552                     this.selectPrevious(e.shiftKey);
35553                 }else if(this.last !== false && this.lastActive !== false){
35554                     var last = this.last;
35555                     this.selectRange(this.last,  this.lastActive-1);
35556                     this.grid.getView().focusRow(this.lastActive);
35557                     if(last !== false){
35558                         this.last = last;
35559                     }
35560                 }else{
35561                     this.selectFirstRow();
35562                 }
35563                 this.fireEvent("afterselectionchange", this);
35564             },
35565             "down" : function(e){
35566                 if(!e.shiftKey){
35567                     this.selectNext(e.shiftKey);
35568                 }else if(this.last !== false && this.lastActive !== false){
35569                     var last = this.last;
35570                     this.selectRange(this.last,  this.lastActive+1);
35571                     this.grid.getView().focusRow(this.lastActive);
35572                     if(last !== false){
35573                         this.last = last;
35574                     }
35575                 }else{
35576                     this.selectFirstRow();
35577                 }
35578                 this.fireEvent("afterselectionchange", this);
35579             },
35580             scope: this
35581         });
35582
35583         var view = this.grid.view;
35584         view.on("refresh", this.onRefresh, this);
35585         view.on("rowupdated", this.onRowUpdated, this);
35586         view.on("rowremoved", this.onRemove, this);
35587     },
35588
35589     // private
35590     onRefresh : function(){
35591         var ds = this.grid.dataSource, i, v = this.grid.view;
35592         var s = this.selections;
35593         s.each(function(r){
35594             if((i = ds.indexOfId(r.id)) != -1){
35595                 v.onRowSelect(i);
35596             }else{
35597                 s.remove(r);
35598             }
35599         });
35600     },
35601
35602     // private
35603     onRemove : function(v, index, r){
35604         this.selections.remove(r);
35605     },
35606
35607     // private
35608     onRowUpdated : function(v, index, r){
35609         if(this.isSelected(r)){
35610             v.onRowSelect(index);
35611         }
35612     },
35613
35614     /**
35615      * Select records.
35616      * @param {Array} records The records to select
35617      * @param {Boolean} keepExisting (optional) True to keep existing selections
35618      */
35619     selectRecords : function(records, keepExisting){
35620         if(!keepExisting){
35621             this.clearSelections();
35622         }
35623         var ds = this.grid.dataSource;
35624         for(var i = 0, len = records.length; i < len; i++){
35625             this.selectRow(ds.indexOf(records[i]), true);
35626         }
35627     },
35628
35629     /**
35630      * Gets the number of selected rows.
35631      * @return {Number}
35632      */
35633     getCount : function(){
35634         return this.selections.length;
35635     },
35636
35637     /**
35638      * Selects the first row in the grid.
35639      */
35640     selectFirstRow : function(){
35641         this.selectRow(0);
35642     },
35643
35644     /**
35645      * Select the last row.
35646      * @param {Boolean} keepExisting (optional) True to keep existing selections
35647      */
35648     selectLastRow : function(keepExisting){
35649         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35650     },
35651
35652     /**
35653      * Selects the row immediately following the last selected row.
35654      * @param {Boolean} keepExisting (optional) True to keep existing selections
35655      */
35656     selectNext : function(keepExisting){
35657         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35658             this.selectRow(this.last+1, keepExisting);
35659             this.grid.getView().focusRow(this.last);
35660         }
35661     },
35662
35663     /**
35664      * Selects the row that precedes the last selected row.
35665      * @param {Boolean} keepExisting (optional) True to keep existing selections
35666      */
35667     selectPrevious : function(keepExisting){
35668         if(this.last){
35669             this.selectRow(this.last-1, keepExisting);
35670             this.grid.getView().focusRow(this.last);
35671         }
35672     },
35673
35674     /**
35675      * Returns the selected records
35676      * @return {Array} Array of selected records
35677      */
35678     getSelections : function(){
35679         return [].concat(this.selections.items);
35680     },
35681
35682     /**
35683      * Returns the first selected record.
35684      * @return {Record}
35685      */
35686     getSelected : function(){
35687         return this.selections.itemAt(0);
35688     },
35689
35690
35691     /**
35692      * Clears all selections.
35693      */
35694     clearSelections : function(fast){
35695         if(this.locked) return;
35696         if(fast !== true){
35697             var ds = this.grid.dataSource;
35698             var s = this.selections;
35699             s.each(function(r){
35700                 this.deselectRow(ds.indexOfId(r.id));
35701             }, this);
35702             s.clear();
35703         }else{
35704             this.selections.clear();
35705         }
35706         this.last = false;
35707     },
35708
35709
35710     /**
35711      * Selects all rows.
35712      */
35713     selectAll : function(){
35714         if(this.locked) return;
35715         this.selections.clear();
35716         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35717             this.selectRow(i, true);
35718         }
35719     },
35720
35721     /**
35722      * Returns True if there is a selection.
35723      * @return {Boolean}
35724      */
35725     hasSelection : function(){
35726         return this.selections.length > 0;
35727     },
35728
35729     /**
35730      * Returns True if the specified row is selected.
35731      * @param {Number/Record} record The record or index of the record to check
35732      * @return {Boolean}
35733      */
35734     isSelected : function(index){
35735         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35736         return (r && this.selections.key(r.id) ? true : false);
35737     },
35738
35739     /**
35740      * Returns True if the specified record id is selected.
35741      * @param {String} id The id of record to check
35742      * @return {Boolean}
35743      */
35744     isIdSelected : function(id){
35745         return (this.selections.key(id) ? true : false);
35746     },
35747
35748     // private
35749     handleMouseDown : function(e, t){
35750         var view = this.grid.getView(), rowIndex;
35751         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35752             return;
35753         };
35754         if(e.shiftKey && this.last !== false){
35755             var last = this.last;
35756             this.selectRange(last, rowIndex, e.ctrlKey);
35757             this.last = last; // reset the last
35758             view.focusRow(rowIndex);
35759         }else{
35760             var isSelected = this.isSelected(rowIndex);
35761             if(e.button !== 0 && isSelected){
35762                 view.focusRow(rowIndex);
35763             }else if(e.ctrlKey && isSelected){
35764                 this.deselectRow(rowIndex);
35765             }else if(!isSelected){
35766                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35767                 view.focusRow(rowIndex);
35768             }
35769         }
35770         this.fireEvent("afterselectionchange", this);
35771     },
35772     // private
35773     handleDragableRowClick :  function(grid, rowIndex, e) 
35774     {
35775         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35776             this.selectRow(rowIndex, false);
35777             grid.view.focusRow(rowIndex);
35778              this.fireEvent("afterselectionchange", this);
35779         }
35780     },
35781     
35782     /**
35783      * Selects multiple rows.
35784      * @param {Array} rows Array of the indexes of the row to select
35785      * @param {Boolean} keepExisting (optional) True to keep existing selections
35786      */
35787     selectRows : function(rows, keepExisting){
35788         if(!keepExisting){
35789             this.clearSelections();
35790         }
35791         for(var i = 0, len = rows.length; i < len; i++){
35792             this.selectRow(rows[i], true);
35793         }
35794     },
35795
35796     /**
35797      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35798      * @param {Number} startRow The index of the first row in the range
35799      * @param {Number} endRow The index of the last row in the range
35800      * @param {Boolean} keepExisting (optional) True to retain existing selections
35801      */
35802     selectRange : function(startRow, endRow, keepExisting){
35803         if(this.locked) return;
35804         if(!keepExisting){
35805             this.clearSelections();
35806         }
35807         if(startRow <= endRow){
35808             for(var i = startRow; i <= endRow; i++){
35809                 this.selectRow(i, true);
35810             }
35811         }else{
35812             for(var i = startRow; i >= endRow; i--){
35813                 this.selectRow(i, true);
35814             }
35815         }
35816     },
35817
35818     /**
35819      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35820      * @param {Number} startRow The index of the first row in the range
35821      * @param {Number} endRow The index of the last row in the range
35822      */
35823     deselectRange : function(startRow, endRow, preventViewNotify){
35824         if(this.locked) return;
35825         for(var i = startRow; i <= endRow; i++){
35826             this.deselectRow(i, preventViewNotify);
35827         }
35828     },
35829
35830     /**
35831      * Selects a row.
35832      * @param {Number} row The index of the row to select
35833      * @param {Boolean} keepExisting (optional) True to keep existing selections
35834      */
35835     selectRow : function(index, keepExisting, preventViewNotify){
35836         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35837         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35838             if(!keepExisting || this.singleSelect){
35839                 this.clearSelections();
35840             }
35841             var r = this.grid.dataSource.getAt(index);
35842             this.selections.add(r);
35843             this.last = this.lastActive = index;
35844             if(!preventViewNotify){
35845                 this.grid.getView().onRowSelect(index);
35846             }
35847             this.fireEvent("rowselect", this, index, r);
35848             this.fireEvent("selectionchange", this);
35849         }
35850     },
35851
35852     /**
35853      * Deselects a row.
35854      * @param {Number} row The index of the row to deselect
35855      */
35856     deselectRow : function(index, preventViewNotify){
35857         if(this.locked) return;
35858         if(this.last == index){
35859             this.last = false;
35860         }
35861         if(this.lastActive == index){
35862             this.lastActive = false;
35863         }
35864         var r = this.grid.dataSource.getAt(index);
35865         this.selections.remove(r);
35866         if(!preventViewNotify){
35867             this.grid.getView().onRowDeselect(index);
35868         }
35869         this.fireEvent("rowdeselect", this, index);
35870         this.fireEvent("selectionchange", this);
35871     },
35872
35873     // private
35874     restoreLast : function(){
35875         if(this._last){
35876             this.last = this._last;
35877         }
35878     },
35879
35880     // private
35881     acceptsNav : function(row, col, cm){
35882         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35883     },
35884
35885     // private
35886     onEditorKey : function(field, e){
35887         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35888         if(k == e.TAB){
35889             e.stopEvent();
35890             ed.completeEdit();
35891             if(e.shiftKey){
35892                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35893             }else{
35894                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35895             }
35896         }else if(k == e.ENTER && !e.ctrlKey){
35897             e.stopEvent();
35898             ed.completeEdit();
35899             if(e.shiftKey){
35900                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35901             }else{
35902                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35903             }
35904         }else if(k == e.ESC){
35905             ed.cancelEdit();
35906         }
35907         if(newCell){
35908             g.startEditing(newCell[0], newCell[1]);
35909         }
35910     }
35911 });/*
35912  * Based on:
35913  * Ext JS Library 1.1.1
35914  * Copyright(c) 2006-2007, Ext JS, LLC.
35915  *
35916  * Originally Released Under LGPL - original licence link has changed is not relivant.
35917  *
35918  * Fork - LGPL
35919  * <script type="text/javascript">
35920  */
35921 /**
35922  * @class Roo.grid.CellSelectionModel
35923  * @extends Roo.grid.AbstractSelectionModel
35924  * This class provides the basic implementation for cell selection in a grid.
35925  * @constructor
35926  * @param {Object} config The object containing the configuration of this model.
35927  */
35928 Roo.grid.CellSelectionModel = function(config){
35929     Roo.apply(this, config);
35930
35931     this.selection = null;
35932
35933     this.addEvents({
35934         /**
35935              * @event beforerowselect
35936              * Fires before a cell is selected.
35937              * @param {SelectionModel} this
35938              * @param {Number} rowIndex The selected row index
35939              * @param {Number} colIndex The selected cell index
35940              */
35941             "beforecellselect" : true,
35942         /**
35943              * @event cellselect
35944              * Fires when a cell is selected.
35945              * @param {SelectionModel} this
35946              * @param {Number} rowIndex The selected row index
35947              * @param {Number} colIndex The selected cell index
35948              */
35949             "cellselect" : true,
35950         /**
35951              * @event selectionchange
35952              * Fires when the active selection changes.
35953              * @param {SelectionModel} this
35954              * @param {Object} selection null for no selection or an object (o) with two properties
35955                 <ul>
35956                 <li>o.record: the record object for the row the selection is in</li>
35957                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35958                 </ul>
35959              */
35960             "selectionchange" : true
35961     });
35962     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35963 };
35964
35965 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35966
35967     /** @ignore */
35968     initEvents : function(){
35969         this.grid.on("mousedown", this.handleMouseDown, this);
35970         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35971         var view = this.grid.view;
35972         view.on("refresh", this.onViewChange, this);
35973         view.on("rowupdated", this.onRowUpdated, this);
35974         view.on("beforerowremoved", this.clearSelections, this);
35975         view.on("beforerowsinserted", this.clearSelections, this);
35976         if(this.grid.isEditor){
35977             this.grid.on("beforeedit", this.beforeEdit,  this);
35978         }
35979     },
35980
35981         //private
35982     beforeEdit : function(e){
35983         this.select(e.row, e.column, false, true, e.record);
35984     },
35985
35986         //private
35987     onRowUpdated : function(v, index, r){
35988         if(this.selection && this.selection.record == r){
35989             v.onCellSelect(index, this.selection.cell[1]);
35990         }
35991     },
35992
35993         //private
35994     onViewChange : function(){
35995         this.clearSelections(true);
35996     },
35997
35998         /**
35999          * Returns the currently selected cell,.
36000          * @return {Array} The selected cell (row, column) or null if none selected.
36001          */
36002     getSelectedCell : function(){
36003         return this.selection ? this.selection.cell : null;
36004     },
36005
36006     /**
36007      * Clears all selections.
36008      * @param {Boolean} true to prevent the gridview from being notified about the change.
36009      */
36010     clearSelections : function(preventNotify){
36011         var s = this.selection;
36012         if(s){
36013             if(preventNotify !== true){
36014                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36015             }
36016             this.selection = null;
36017             this.fireEvent("selectionchange", this, null);
36018         }
36019     },
36020
36021     /**
36022      * Returns true if there is a selection.
36023      * @return {Boolean}
36024      */
36025     hasSelection : function(){
36026         return this.selection ? true : false;
36027     },
36028
36029     /** @ignore */
36030     handleMouseDown : function(e, t){
36031         var v = this.grid.getView();
36032         if(this.isLocked()){
36033             return;
36034         };
36035         var row = v.findRowIndex(t);
36036         var cell = v.findCellIndex(t);
36037         if(row !== false && cell !== false){
36038             this.select(row, cell);
36039         }
36040     },
36041
36042     /**
36043      * Selects a cell.
36044      * @param {Number} rowIndex
36045      * @param {Number} collIndex
36046      */
36047     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36048         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36049             this.clearSelections();
36050             r = r || this.grid.dataSource.getAt(rowIndex);
36051             this.selection = {
36052                 record : r,
36053                 cell : [rowIndex, colIndex]
36054             };
36055             if(!preventViewNotify){
36056                 var v = this.grid.getView();
36057                 v.onCellSelect(rowIndex, colIndex);
36058                 if(preventFocus !== true){
36059                     v.focusCell(rowIndex, colIndex);
36060                 }
36061             }
36062             this.fireEvent("cellselect", this, rowIndex, colIndex);
36063             this.fireEvent("selectionchange", this, this.selection);
36064         }
36065     },
36066
36067         //private
36068     isSelectable : function(rowIndex, colIndex, cm){
36069         return !cm.isHidden(colIndex);
36070     },
36071
36072     /** @ignore */
36073     handleKeyDown : function(e){
36074         Roo.log('Cell Sel Model handleKeyDown');
36075         if(!e.isNavKeyPress()){
36076             return;
36077         }
36078         var g = this.grid, s = this.selection;
36079         if(!s){
36080             e.stopEvent();
36081             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36082             if(cell){
36083                 this.select(cell[0], cell[1]);
36084             }
36085             return;
36086         }
36087         var sm = this;
36088         var walk = function(row, col, step){
36089             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36090         };
36091         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36092         var newCell;
36093
36094         switch(k){
36095             case e.TAB:
36096                 // handled by onEditorKey
36097                 if (g.isEditor && g.editing) {
36098                     return;
36099                 }
36100                 if(e.shiftKey){
36101                      newCell = walk(r, c-1, -1);
36102                 }else{
36103                      newCell = walk(r, c+1, 1);
36104                 }
36105              break;
36106              case e.DOWN:
36107                  newCell = walk(r+1, c, 1);
36108              break;
36109              case e.UP:
36110                  newCell = walk(r-1, c, -1);
36111              break;
36112              case e.RIGHT:
36113                  newCell = walk(r, c+1, 1);
36114              break;
36115              case e.LEFT:
36116                  newCell = walk(r, c-1, -1);
36117              break;
36118              case e.ENTER:
36119                  if(g.isEditor && !g.editing){
36120                     g.startEditing(r, c);
36121                     e.stopEvent();
36122                     return;
36123                 }
36124              break;
36125         };
36126         if(newCell){
36127             this.select(newCell[0], newCell[1]);
36128             e.stopEvent();
36129         }
36130     },
36131
36132     acceptsNav : function(row, col, cm){
36133         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36134     },
36135
36136     onEditorKey : function(field, e){
36137         
36138         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36139         ///Roo.log('onEditorKey' + k);
36140         
36141         if(k == e.TAB){
36142             if(e.shiftKey){
36143                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36144             }else{
36145                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36146             }
36147             e.stopEvent();
36148         }else if(k == e.ENTER && !e.ctrlKey){
36149             ed.completeEdit();
36150             e.stopEvent();
36151             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36152         }else if(k == e.ESC){
36153             ed.cancelEdit();
36154         }
36155         
36156         
36157         if(newCell){
36158             //Roo.log('next cell after edit');
36159             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36160         }
36161     }
36162 });/*
36163  * Based on:
36164  * Ext JS Library 1.1.1
36165  * Copyright(c) 2006-2007, Ext JS, LLC.
36166  *
36167  * Originally Released Under LGPL - original licence link has changed is not relivant.
36168  *
36169  * Fork - LGPL
36170  * <script type="text/javascript">
36171  */
36172  
36173 /**
36174  * @class Roo.grid.EditorGrid
36175  * @extends Roo.grid.Grid
36176  * Class for creating and editable grid.
36177  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36178  * The container MUST have some type of size defined for the grid to fill. The container will be 
36179  * automatically set to position relative if it isn't already.
36180  * @param {Object} dataSource The data model to bind to
36181  * @param {Object} colModel The column model with info about this grid's columns
36182  */
36183 Roo.grid.EditorGrid = function(container, config){
36184     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36185     this.getGridEl().addClass("xedit-grid");
36186
36187     if(!this.selModel){
36188         this.selModel = new Roo.grid.CellSelectionModel();
36189     }
36190
36191     this.activeEditor = null;
36192
36193         this.addEvents({
36194             /**
36195              * @event beforeedit
36196              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36197              * <ul style="padding:5px;padding-left:16px;">
36198              * <li>grid - This grid</li>
36199              * <li>record - The record being edited</li>
36200              * <li>field - The field name being edited</li>
36201              * <li>value - The value for the field being edited.</li>
36202              * <li>row - The grid row index</li>
36203              * <li>column - The grid column index</li>
36204              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36205              * </ul>
36206              * @param {Object} e An edit event (see above for description)
36207              */
36208             "beforeedit" : true,
36209             /**
36210              * @event afteredit
36211              * Fires after a cell is edited. <br />
36212              * <ul style="padding:5px;padding-left:16px;">
36213              * <li>grid - This grid</li>
36214              * <li>record - The record being edited</li>
36215              * <li>field - The field name being edited</li>
36216              * <li>value - The value being set</li>
36217              * <li>originalValue - The original value for the field, before the edit.</li>
36218              * <li>row - The grid row index</li>
36219              * <li>column - The grid column index</li>
36220              * </ul>
36221              * @param {Object} e An edit event (see above for description)
36222              */
36223             "afteredit" : true,
36224             /**
36225              * @event validateedit
36226              * Fires after a cell is edited, but before the value is set in the record. 
36227          * You can use this to modify the value being set in the field, Return false
36228              * to cancel the change. The edit event object has the following properties <br />
36229              * <ul style="padding:5px;padding-left:16px;">
36230          * <li>editor - This editor</li>
36231              * <li>grid - This grid</li>
36232              * <li>record - The record being edited</li>
36233              * <li>field - The field name being edited</li>
36234              * <li>value - The value being set</li>
36235              * <li>originalValue - The original value for the field, before the edit.</li>
36236              * <li>row - The grid row index</li>
36237              * <li>column - The grid column index</li>
36238              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36239              * </ul>
36240              * @param {Object} e An edit event (see above for description)
36241              */
36242             "validateedit" : true
36243         });
36244     this.on("bodyscroll", this.stopEditing,  this);
36245     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36246 };
36247
36248 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36249     /**
36250      * @cfg {Number} clicksToEdit
36251      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36252      */
36253     clicksToEdit: 2,
36254
36255     // private
36256     isEditor : true,
36257     // private
36258     trackMouseOver: false, // causes very odd FF errors
36259
36260     onCellDblClick : function(g, row, col){
36261         this.startEditing(row, col);
36262     },
36263
36264     onEditComplete : function(ed, value, startValue){
36265         this.editing = false;
36266         this.activeEditor = null;
36267         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36268         var r = ed.record;
36269         var field = this.colModel.getDataIndex(ed.col);
36270         var e = {
36271             grid: this,
36272             record: r,
36273             field: field,
36274             originalValue: startValue,
36275             value: value,
36276             row: ed.row,
36277             column: ed.col,
36278             cancel:false,
36279             editor: ed
36280         };
36281         if(String(value) !== String(startValue)){
36282             
36283             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36284                 r.set(field, e.value);
36285                 // if we are dealing with a combo box..
36286                 // then we also set the 'name' colum to be the displayField
36287                 if (ed.field.displayField && ed.field.name) {
36288                     r.set(ed.field.name, ed.field.el.dom.value);
36289                 }
36290                 
36291                 delete e.cancel; //?? why!!!
36292                 this.fireEvent("afteredit", e);
36293             }
36294         } else {
36295             this.fireEvent("afteredit", e); // always fire it!
36296         }
36297         this.view.focusCell(ed.row, ed.col);
36298     },
36299
36300     /**
36301      * Starts editing the specified for the specified row/column
36302      * @param {Number} rowIndex
36303      * @param {Number} colIndex
36304      */
36305     startEditing : function(row, col){
36306         this.stopEditing();
36307         if(this.colModel.isCellEditable(col, row)){
36308             this.view.ensureVisible(row, col, true);
36309             var r = this.dataSource.getAt(row);
36310             var field = this.colModel.getDataIndex(col);
36311             var e = {
36312                 grid: this,
36313                 record: r,
36314                 field: field,
36315                 value: r.data[field],
36316                 row: row,
36317                 column: col,
36318                 cancel:false
36319             };
36320             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36321                 this.editing = true;
36322                 var ed = this.colModel.getCellEditor(col, row);
36323                 
36324                 if (!ed) {
36325                     return;
36326                 }
36327                 if(!ed.rendered){
36328                     ed.render(ed.parentEl || document.body);
36329                 }
36330                 ed.field.reset();
36331                 (function(){ // complex but required for focus issues in safari, ie and opera
36332                     ed.row = row;
36333                     ed.col = col;
36334                     ed.record = r;
36335                     ed.on("complete", this.onEditComplete, this, {single: true});
36336                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36337                     this.activeEditor = ed;
36338                     var v = r.data[field];
36339                     ed.startEdit(this.view.getCell(row, col), v);
36340                     // combo's with 'displayField and name set
36341                     if (ed.field.displayField && ed.field.name) {
36342                         ed.field.el.dom.value = r.data[ed.field.name];
36343                     }
36344                     
36345                     
36346                 }).defer(50, this);
36347             }
36348         }
36349     },
36350         
36351     /**
36352      * Stops any active editing
36353      */
36354     stopEditing : function(){
36355         if(this.activeEditor){
36356             this.activeEditor.completeEdit();
36357         }
36358         this.activeEditor = null;
36359     }
36360 });/*
36361  * Based on:
36362  * Ext JS Library 1.1.1
36363  * Copyright(c) 2006-2007, Ext JS, LLC.
36364  *
36365  * Originally Released Under LGPL - original licence link has changed is not relivant.
36366  *
36367  * Fork - LGPL
36368  * <script type="text/javascript">
36369  */
36370
36371 // private - not really -- you end up using it !
36372 // This is a support class used internally by the Grid components
36373
36374 /**
36375  * @class Roo.grid.GridEditor
36376  * @extends Roo.Editor
36377  * Class for creating and editable grid elements.
36378  * @param {Object} config any settings (must include field)
36379  */
36380 Roo.grid.GridEditor = function(field, config){
36381     if (!config && field.field) {
36382         config = field;
36383         field = Roo.factory(config.field, Roo.form);
36384     }
36385     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36386     field.monitorTab = false;
36387 };
36388
36389 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36390     
36391     /**
36392      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36393      */
36394     
36395     alignment: "tl-tl",
36396     autoSize: "width",
36397     hideEl : false,
36398     cls: "x-small-editor x-grid-editor",
36399     shim:false,
36400     shadow:"frame"
36401 });/*
36402  * Based on:
36403  * Ext JS Library 1.1.1
36404  * Copyright(c) 2006-2007, Ext JS, LLC.
36405  *
36406  * Originally Released Under LGPL - original licence link has changed is not relivant.
36407  *
36408  * Fork - LGPL
36409  * <script type="text/javascript">
36410  */
36411   
36412
36413   
36414 Roo.grid.PropertyRecord = Roo.data.Record.create([
36415     {name:'name',type:'string'},  'value'
36416 ]);
36417
36418
36419 Roo.grid.PropertyStore = function(grid, source){
36420     this.grid = grid;
36421     this.store = new Roo.data.Store({
36422         recordType : Roo.grid.PropertyRecord
36423     });
36424     this.store.on('update', this.onUpdate,  this);
36425     if(source){
36426         this.setSource(source);
36427     }
36428     Roo.grid.PropertyStore.superclass.constructor.call(this);
36429 };
36430
36431
36432
36433 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36434     setSource : function(o){
36435         this.source = o;
36436         this.store.removeAll();
36437         var data = [];
36438         for(var k in o){
36439             if(this.isEditableValue(o[k])){
36440                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36441             }
36442         }
36443         this.store.loadRecords({records: data}, {}, true);
36444     },
36445
36446     onUpdate : function(ds, record, type){
36447         if(type == Roo.data.Record.EDIT){
36448             var v = record.data['value'];
36449             var oldValue = record.modified['value'];
36450             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36451                 this.source[record.id] = v;
36452                 record.commit();
36453                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36454             }else{
36455                 record.reject();
36456             }
36457         }
36458     },
36459
36460     getProperty : function(row){
36461        return this.store.getAt(row);
36462     },
36463
36464     isEditableValue: function(val){
36465         if(val && val instanceof Date){
36466             return true;
36467         }else if(typeof val == 'object' || typeof val == 'function'){
36468             return false;
36469         }
36470         return true;
36471     },
36472
36473     setValue : function(prop, value){
36474         this.source[prop] = value;
36475         this.store.getById(prop).set('value', value);
36476     },
36477
36478     getSource : function(){
36479         return this.source;
36480     }
36481 });
36482
36483 Roo.grid.PropertyColumnModel = function(grid, store){
36484     this.grid = grid;
36485     var g = Roo.grid;
36486     g.PropertyColumnModel.superclass.constructor.call(this, [
36487         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36488         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36489     ]);
36490     this.store = store;
36491     this.bselect = Roo.DomHelper.append(document.body, {
36492         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36493             {tag: 'option', value: 'true', html: 'true'},
36494             {tag: 'option', value: 'false', html: 'false'}
36495         ]
36496     });
36497     Roo.id(this.bselect);
36498     var f = Roo.form;
36499     this.editors = {
36500         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36501         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36502         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36503         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36504         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36505     };
36506     this.renderCellDelegate = this.renderCell.createDelegate(this);
36507     this.renderPropDelegate = this.renderProp.createDelegate(this);
36508 };
36509
36510 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36511     
36512     
36513     nameText : 'Name',
36514     valueText : 'Value',
36515     
36516     dateFormat : 'm/j/Y',
36517     
36518     
36519     renderDate : function(dateVal){
36520         return dateVal.dateFormat(this.dateFormat);
36521     },
36522
36523     renderBool : function(bVal){
36524         return bVal ? 'true' : 'false';
36525     },
36526
36527     isCellEditable : function(colIndex, rowIndex){
36528         return colIndex == 1;
36529     },
36530
36531     getRenderer : function(col){
36532         return col == 1 ?
36533             this.renderCellDelegate : this.renderPropDelegate;
36534     },
36535
36536     renderProp : function(v){
36537         return this.getPropertyName(v);
36538     },
36539
36540     renderCell : function(val){
36541         var rv = val;
36542         if(val instanceof Date){
36543             rv = this.renderDate(val);
36544         }else if(typeof val == 'boolean'){
36545             rv = this.renderBool(val);
36546         }
36547         return Roo.util.Format.htmlEncode(rv);
36548     },
36549
36550     getPropertyName : function(name){
36551         var pn = this.grid.propertyNames;
36552         return pn && pn[name] ? pn[name] : name;
36553     },
36554
36555     getCellEditor : function(colIndex, rowIndex){
36556         var p = this.store.getProperty(rowIndex);
36557         var n = p.data['name'], val = p.data['value'];
36558         
36559         if(typeof(this.grid.customEditors[n]) == 'string'){
36560             return this.editors[this.grid.customEditors[n]];
36561         }
36562         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36563             return this.grid.customEditors[n];
36564         }
36565         if(val instanceof Date){
36566             return this.editors['date'];
36567         }else if(typeof val == 'number'){
36568             return this.editors['number'];
36569         }else if(typeof val == 'boolean'){
36570             return this.editors['boolean'];
36571         }else{
36572             return this.editors['string'];
36573         }
36574     }
36575 });
36576
36577 /**
36578  * @class Roo.grid.PropertyGrid
36579  * @extends Roo.grid.EditorGrid
36580  * This class represents the  interface of a component based property grid control.
36581  * <br><br>Usage:<pre><code>
36582  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36583       
36584  });
36585  // set any options
36586  grid.render();
36587  * </code></pre>
36588   
36589  * @constructor
36590  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36591  * The container MUST have some type of size defined for the grid to fill. The container will be
36592  * automatically set to position relative if it isn't already.
36593  * @param {Object} config A config object that sets properties on this grid.
36594  */
36595 Roo.grid.PropertyGrid = function(container, config){
36596     config = config || {};
36597     var store = new Roo.grid.PropertyStore(this);
36598     this.store = store;
36599     var cm = new Roo.grid.PropertyColumnModel(this, store);
36600     store.store.sort('name', 'ASC');
36601     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36602         ds: store.store,
36603         cm: cm,
36604         enableColLock:false,
36605         enableColumnMove:false,
36606         stripeRows:false,
36607         trackMouseOver: false,
36608         clicksToEdit:1
36609     }, config));
36610     this.getGridEl().addClass('x-props-grid');
36611     this.lastEditRow = null;
36612     this.on('columnresize', this.onColumnResize, this);
36613     this.addEvents({
36614          /**
36615              * @event beforepropertychange
36616              * Fires before a property changes (return false to stop?)
36617              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36618              * @param {String} id Record Id
36619              * @param {String} newval New Value
36620          * @param {String} oldval Old Value
36621              */
36622         "beforepropertychange": true,
36623         /**
36624              * @event propertychange
36625              * Fires after a property changes
36626              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36627              * @param {String} id Record Id
36628              * @param {String} newval New Value
36629          * @param {String} oldval Old Value
36630              */
36631         "propertychange": true
36632     });
36633     this.customEditors = this.customEditors || {};
36634 };
36635 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36636     
36637      /**
36638      * @cfg {Object} customEditors map of colnames=> custom editors.
36639      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36640      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36641      * false disables editing of the field.
36642          */
36643     
36644       /**
36645      * @cfg {Object} propertyNames map of property Names to their displayed value
36646          */
36647     
36648     render : function(){
36649         Roo.grid.PropertyGrid.superclass.render.call(this);
36650         this.autoSize.defer(100, this);
36651     },
36652
36653     autoSize : function(){
36654         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36655         if(this.view){
36656             this.view.fitColumns();
36657         }
36658     },
36659
36660     onColumnResize : function(){
36661         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36662         this.autoSize();
36663     },
36664     /**
36665      * Sets the data for the Grid
36666      * accepts a Key => Value object of all the elements avaiable.
36667      * @param {Object} data  to appear in grid.
36668      */
36669     setSource : function(source){
36670         this.store.setSource(source);
36671         //this.autoSize();
36672     },
36673     /**
36674      * Gets all the data from the grid.
36675      * @return {Object} data  data stored in grid
36676      */
36677     getSource : function(){
36678         return this.store.getSource();
36679     }
36680 });/*
36681  * Based on:
36682  * Ext JS Library 1.1.1
36683  * Copyright(c) 2006-2007, Ext JS, LLC.
36684  *
36685  * Originally Released Under LGPL - original licence link has changed is not relivant.
36686  *
36687  * Fork - LGPL
36688  * <script type="text/javascript">
36689  */
36690  
36691 /**
36692  * @class Roo.LoadMask
36693  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36694  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36695  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36696  * element's UpdateManager load indicator and will be destroyed after the initial load.
36697  * @constructor
36698  * Create a new LoadMask
36699  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36700  * @param {Object} config The config object
36701  */
36702 Roo.LoadMask = function(el, config){
36703     this.el = Roo.get(el);
36704     Roo.apply(this, config);
36705     if(this.store){
36706         this.store.on('beforeload', this.onBeforeLoad, this);
36707         this.store.on('load', this.onLoad, this);
36708         this.store.on('loadexception', this.onLoad, this);
36709         this.removeMask = false;
36710     }else{
36711         var um = this.el.getUpdateManager();
36712         um.showLoadIndicator = false; // disable the default indicator
36713         um.on('beforeupdate', this.onBeforeLoad, this);
36714         um.on('update', this.onLoad, this);
36715         um.on('failure', this.onLoad, this);
36716         this.removeMask = true;
36717     }
36718 };
36719
36720 Roo.LoadMask.prototype = {
36721     /**
36722      * @cfg {Boolean} removeMask
36723      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36724      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36725      */
36726     /**
36727      * @cfg {String} msg
36728      * The text to display in a centered loading message box (defaults to 'Loading...')
36729      */
36730     msg : 'Loading...',
36731     /**
36732      * @cfg {String} msgCls
36733      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36734      */
36735     msgCls : 'x-mask-loading',
36736
36737     /**
36738      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36739      * @type Boolean
36740      */
36741     disabled: false,
36742
36743     /**
36744      * Disables the mask to prevent it from being displayed
36745      */
36746     disable : function(){
36747        this.disabled = true;
36748     },
36749
36750     /**
36751      * Enables the mask so that it can be displayed
36752      */
36753     enable : function(){
36754         this.disabled = false;
36755     },
36756
36757     // private
36758     onLoad : function(){
36759         this.el.unmask(this.removeMask);
36760     },
36761
36762     // private
36763     onBeforeLoad : function(){
36764         if(!this.disabled){
36765             this.el.mask(this.msg, this.msgCls);
36766         }
36767     },
36768
36769     // private
36770     destroy : function(){
36771         if(this.store){
36772             this.store.un('beforeload', this.onBeforeLoad, this);
36773             this.store.un('load', this.onLoad, this);
36774             this.store.un('loadexception', this.onLoad, this);
36775         }else{
36776             var um = this.el.getUpdateManager();
36777             um.un('beforeupdate', this.onBeforeLoad, this);
36778             um.un('update', this.onLoad, this);
36779             um.un('failure', this.onLoad, this);
36780         }
36781     }
36782 };/*
36783  * Based on:
36784  * Ext JS Library 1.1.1
36785  * Copyright(c) 2006-2007, Ext JS, LLC.
36786  *
36787  * Originally Released Under LGPL - original licence link has changed is not relivant.
36788  *
36789  * Fork - LGPL
36790  * <script type="text/javascript">
36791  */
36792 Roo.XTemplate = function(){
36793     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36794     var s = this.html;
36795
36796     s = ['<tpl>', s, '</tpl>'].join('');
36797
36798     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36799
36800     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36801     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36802     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36803     var m, id = 0;
36804     var tpls = [];
36805
36806     while(m = s.match(re)){
36807        var m2 = m[0].match(nameRe);
36808        var m3 = m[0].match(ifRe);
36809        var m4 = m[0].match(execRe);
36810        var exp = null, fn = null, exec = null;
36811        var name = m2 && m2[1] ? m2[1] : '';
36812        if(m3){
36813            exp = m3 && m3[1] ? m3[1] : null;
36814            if(exp){
36815                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36816            }
36817        }
36818        if(m4){
36819            exp = m4 && m4[1] ? m4[1] : null;
36820            if(exp){
36821                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36822            }
36823        }
36824        if(name){
36825            switch(name){
36826                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36827                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36828                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36829            }
36830        }
36831        tpls.push({
36832             id: id,
36833             target: name,
36834             exec: exec,
36835             test: fn,
36836             body: m[1]||''
36837         });
36838        s = s.replace(m[0], '{xtpl'+ id + '}');
36839        ++id;
36840     }
36841     for(var i = tpls.length-1; i >= 0; --i){
36842         this.compileTpl(tpls[i]);
36843     }
36844     this.master = tpls[tpls.length-1];
36845     this.tpls = tpls;
36846 };
36847 Roo.extend(Roo.XTemplate, Roo.Template, {
36848
36849     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36850
36851     applySubTemplate : function(id, values, parent){
36852         var t = this.tpls[id];
36853         if(t.test && !t.test.call(this, values, parent)){
36854             return '';
36855         }
36856         if(t.exec && t.exec.call(this, values, parent)){
36857             return '';
36858         }
36859         var vs = t.target ? t.target.call(this, values, parent) : values;
36860         parent = t.target ? values : parent;
36861         if(t.target && vs instanceof Array){
36862             var buf = [];
36863             for(var i = 0, len = vs.length; i < len; i++){
36864                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36865             }
36866             return buf.join('');
36867         }
36868         return t.compiled.call(this, vs, parent);
36869     },
36870
36871     compileTpl : function(tpl){
36872         var fm = Roo.util.Format;
36873         var useF = this.disableFormats !== true;
36874         var sep = Roo.isGecko ? "+" : ",";
36875         var fn = function(m, name, format, args){
36876             if(name.substr(0, 4) == 'xtpl'){
36877                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36878             }
36879             var v;
36880             if(name.indexOf('.') != -1){
36881                 v = name;
36882             }else{
36883                 v = "values['" + name + "']";
36884             }
36885             if(format && useF){
36886                 args = args ? ',' + args : "";
36887                 if(format.substr(0, 5) != "this."){
36888                     format = "fm." + format + '(';
36889                 }else{
36890                     format = 'this.call("'+ format.substr(5) + '", ';
36891                     args = ", values";
36892                 }
36893             }else{
36894                 args= ''; format = "("+v+" === undefined ? '' : ";
36895             }
36896             return "'"+ sep + format + v + args + ")"+sep+"'";
36897         };
36898         var body;
36899         // branched to use + in gecko and [].join() in others
36900         if(Roo.isGecko){
36901             body = "tpl.compiled = function(values, parent){ return '" +
36902                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36903                     "';};";
36904         }else{
36905             body = ["tpl.compiled = function(values, parent){ return ['"];
36906             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36907             body.push("'].join('');};");
36908             body = body.join('');
36909         }
36910         /** eval:var:zzzzzzz */
36911         eval(body);
36912         return this;
36913     },
36914
36915     applyTemplate : function(values){
36916         return this.master.compiled.call(this, values, {});
36917         var s = this.subs;
36918     },
36919
36920     apply : function(){
36921         return this.applyTemplate.apply(this, arguments);
36922     },
36923
36924     compile : function(){return this;}
36925 });
36926
36927 Roo.XTemplate.from = function(el){
36928     el = Roo.getDom(el);
36929     return new Roo.XTemplate(el.value || el.innerHTML);
36930 };/*
36931  * Original code for Roojs - LGPL
36932  * <script type="text/javascript">
36933  */
36934  
36935 /**
36936  * @class Roo.XComponent
36937  * A delayed Element creator...
36938  * Or a way to group chunks of interface together.
36939  * 
36940  * Mypart.xyx = new Roo.XComponent({
36941
36942     parent : 'Mypart.xyz', // empty == document.element.!!
36943     order : '001',
36944     name : 'xxxx'
36945     region : 'xxxx'
36946     disabled : function() {} 
36947      
36948     tree : function() { // return an tree of xtype declared components
36949         var MODULE = this;
36950         return 
36951         {
36952             xtype : 'NestedLayoutPanel',
36953             // technicall
36954         }
36955      ]
36956  *})
36957  *
36958  *
36959  * It can be used to build a big heiracy, with parent etc.
36960  * or you can just use this to render a single compoent to a dom element
36961  * MYPART.render(Roo.Element | String(id) | dom_element )
36962  * 
36963  * @extends Roo.util.Observable
36964  * @constructor
36965  * @param cfg {Object} configuration of component
36966  * 
36967  */
36968 Roo.XComponent = function(cfg) {
36969     Roo.apply(this, cfg);
36970     this.addEvents({ 
36971         /**
36972              * @event built
36973              * Fires when this the componnt is built
36974              * @param {Roo.XComponent} c the component
36975              */
36976         'built' : true,
36977         /**
36978              * @event buildcomplete
36979              * Fires on the top level element when all elements have been built
36980              * @param {Roo.XComponent} c the top level component.
36981          */
36982         'buildcomplete' : true
36983         
36984     });
36985     this.region = this.region || 'center'; // default..
36986     Roo.XComponent.register(this);
36987     this.modules = false;
36988     this.el = false; // where the layout goes..
36989     
36990     
36991 }
36992 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36993     /**
36994      * @property el
36995      * The created element (with Roo.factory())
36996      * @type {Roo.Layout}
36997      */
36998     el  : false,
36999     
37000     /**
37001      * @property el
37002      * for BC  - use el in new code
37003      * @type {Roo.Layout}
37004      */
37005     panel : false,
37006     
37007     /**
37008      * @property layout
37009      * for BC  - use el in new code
37010      * @type {Roo.Layout}
37011      */
37012     layout : false,
37013     
37014      /**
37015      * @cfg {Function|boolean} disabled
37016      * If this module is disabled by some rule, return true from the funtion
37017      */
37018     disabled : false,
37019     
37020     /**
37021      * @cfg {String} parent 
37022      * Name of parent element which it get xtype added to..
37023      */
37024     parent: false,
37025     
37026     /**
37027      * @cfg {String} order
37028      * Used to set the order in which elements are created (usefull for multiple tabs)
37029      */
37030     
37031     order : false,
37032     /**
37033      * @cfg {String} name
37034      * String to display while loading.
37035      */
37036     name : false,
37037     /**
37038      * @cfg {String} region
37039      * Region to render component to (defaults to center)
37040      */
37041     region : 'center',
37042     
37043     /**
37044      * @cfg {Array} items
37045      * A single item array - the first element is the root of the tree..
37046      * It's done this way to stay compatible with the Xtype system...
37047      */
37048     items : false,
37049     
37050     
37051      /**
37052      * render
37053      * render element to dom or tree
37054      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37055      */
37056     
37057     render : function(el)
37058     {
37059         
37060         el = el || false;
37061         var hp = this.parent ? 1 : 0;
37062         
37063         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37064             // if parent is a '#.....' string, then let's use that..
37065             var ename = this.parent.substr(1)
37066             this.parent = false;
37067             el = Roo.get(ename);
37068             if (!el) {
37069                 Roo.log("Warning - element can not be found :#" + ename );
37070                 return;
37071             }
37072         }
37073         
37074         
37075         if (!this.parent) {
37076             
37077             el = el ? Roo.get(el) : false;
37078             
37079             // it's a top level one..
37080             this.parent =  {
37081                 el : new Roo.BorderLayout(el || document.body, {
37082                 
37083                      center: {
37084                          titlebar: false,
37085                          autoScroll:false,
37086                          closeOnTab: true,
37087                          tabPosition: 'top',
37088                           //resizeTabs: true,
37089                          alwaysShowTabs: el && hp? false :  true,
37090                          hideTabs: el || !hp ? true :  false,
37091                          minTabWidth: 140
37092                      }
37093                  })
37094             }
37095         }
37096         
37097         
37098             
37099         var tree = this.tree();
37100         tree.region = tree.region || this.region;
37101         this.el = this.parent.el.addxtype(tree);
37102         this.fireEvent('built', this);
37103         
37104         this.panel = this.el;
37105         this.layout = this.panel.layout;    
37106          
37107     }
37108     
37109 });
37110
37111 Roo.apply(Roo.XComponent, {
37112     
37113     /**
37114      * @property  buildCompleted
37115      * True when the builder has completed building the interface.
37116      * @type Boolean
37117      */
37118     buildCompleted : false,
37119      
37120     /**
37121      * @property  topModule
37122      * the upper most module - uses document.element as it's constructor.
37123      * @type Object
37124      */
37125      
37126     topModule  : false,
37127       
37128     /**
37129      * @property  modules
37130      * array of modules to be created by registration system.
37131      * @type {Array} of Roo.XComponent
37132      */
37133     
37134     modules : [],
37135     /**
37136      * @property  elmodules
37137      * array of modules to be created by which use #ID 
37138      * @type {Array} of Roo.XComponent
37139      */
37140      
37141     elmodules : [],
37142
37143     
37144     /**
37145      * Register components to be built later.
37146      *
37147      * This solves the following issues
37148      * - Building is not done on page load, but after an authentication process has occured.
37149      * - Interface elements are registered on page load
37150      * - Parent Interface elements may not be loaded before child, so this handles that..
37151      * 
37152      *
37153      * example:
37154      * 
37155      * MyApp.register({
37156           order : '000001',
37157           module : 'Pman.Tab.projectMgr',
37158           region : 'center',
37159           parent : 'Pman.layout',
37160           disabled : false,  // or use a function..
37161         })
37162      
37163      * * @param {Object} details about module
37164      */
37165     register : function(obj) {
37166         this.modules.push(obj);
37167          
37168     },
37169     /**
37170      * convert a string to an object..
37171      * eg. 'AAA.BBB' -> finds AAA.BBB
37172
37173      */
37174     
37175     toObject : function(str)
37176     {
37177         if (!str || typeof(str) == 'object') {
37178             return str;
37179         }
37180         if (str.substring(0,1) == '#') {
37181             return str;
37182         }
37183
37184         var ar = str.split('.');
37185         var rt, o;
37186         rt = ar.shift();
37187             /** eval:var:o */
37188         try {
37189             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37190         } catch (e) {
37191             throw "Module not found : " + str;
37192         }
37193         
37194         if (o === false) {
37195             throw "Module not found : " + str;
37196         }
37197         Roo.each(ar, function(e) {
37198             if (typeof(o[e]) == 'undefined') {
37199                 throw "Module not found : " + str;
37200             }
37201             o = o[e];
37202         });
37203         
37204         return o;
37205         
37206     },
37207     
37208     
37209     /**
37210      * move modules into their correct place in the tree..
37211      * 
37212      */
37213     preBuild : function ()
37214     {
37215         var _t = this;
37216         Roo.each(this.modules , function (obj)
37217         {
37218             var opar = obj.parent;
37219             try { 
37220                 obj.parent = this.toObject(opar);
37221             } catch(e) {
37222                 Roo.log(e.toString());
37223                 return;
37224             }
37225             
37226             if (!obj.parent) {
37227                 this.topModule = obj;
37228                 return;
37229             }
37230             if (typeof(obj.parent) == 'string') {
37231                 this.elmodules.push(obj);
37232                 return;
37233             }
37234             if (obj.parent.constructor != Roo.XComponent) {
37235                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
37236             }
37237             if (!obj.parent.modules) {
37238                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37239                     function(o) { return o.order + '' }
37240                 );
37241             }
37242             
37243             obj.parent.modules.add(obj);
37244         }, this);
37245     },
37246     
37247      /**
37248      * make a list of modules to build.
37249      * @return {Array} list of modules. 
37250      */ 
37251     
37252     buildOrder : function()
37253     {
37254         var _this = this;
37255         var cmp = function(a,b) {   
37256             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37257         };
37258         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37259             throw "No top level modules to build";
37260         }
37261         
37262         // make a flat list in order of modules to build.
37263         var mods = this.topModule ? [ this.topModule ] : [];
37264         Roo.each(this.elmodules,function(e) { mods.push(e) });
37265
37266         
37267         // add modules to their parents..
37268         var addMod = function(m) {
37269            // Roo.debug && Roo.log(m.modKey);
37270             
37271             mods.push(m);
37272             if (m.modules) {
37273                 m.modules.keySort('ASC',  cmp );
37274                 m.modules.each(addMod);
37275             }
37276             // not sure if this is used any more..
37277             if (m.finalize) {
37278                 m.finalize.name = m.name + " (clean up) ";
37279                 mods.push(m.finalize);
37280             }
37281             
37282         }
37283         if (this.topModule) { 
37284             this.topModule.modules.keySort('ASC',  cmp );
37285             this.topModule.modules.each(addMod);
37286         }
37287         return mods;
37288     },
37289     
37290      /**
37291      * Build the registered modules.
37292      * @param {Object} parent element.
37293      * @param {Function} optional method to call after module has been added.
37294      * 
37295      */ 
37296    
37297     build : function() 
37298     {
37299         
37300         this.preBuild();
37301         var mods = this.buildOrder();
37302       
37303         //this.allmods = mods;
37304         //Roo.debug && Roo.log(mods);
37305         //return;
37306         if (!mods.length) { // should not happen
37307             throw "NO modules!!!";
37308         }
37309         
37310         
37311         
37312         // flash it up as modal - so we store the mask!?
37313         Roo.MessageBox.show({ title: 'loading' });
37314         Roo.MessageBox.show({
37315            title: "Please wait...",
37316            msg: "Building Interface...",
37317            width:450,
37318            progress:true,
37319            closable:false,
37320            modal: false
37321           
37322         });
37323         var total = mods.length;
37324         
37325         var _this = this;
37326         var progressRun = function() {
37327             if (!mods.length) {
37328                 Roo.debug && Roo.log('hide?');
37329                 Roo.MessageBox.hide();
37330                 if (_this.topModule) { 
37331                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37332                 }
37333                 // THE END...
37334                 return false;   
37335             }
37336             
37337             var m = mods.shift();
37338             
37339             
37340             Roo.debug && Roo.log(m);
37341             // not sure if this is supported any more.. - modules that are are just function
37342             if (typeof(m) == 'function') { 
37343                 m.call(this);
37344                 return progressRun.defer(10, _this);
37345             } 
37346             
37347             
37348             
37349             Roo.MessageBox.updateProgress(
37350                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37351                     " of " + total + 
37352                     (m.name ? (' - ' + m.name) : '')
37353                     );
37354             
37355          
37356             // is the module disabled?
37357             var disabled = (typeof(m.disabled) == 'function') ?
37358                 m.disabled.call(m.module.disabled) : m.disabled;    
37359             
37360             
37361             if (disabled) {
37362                 return progressRun(); // we do not update the display!
37363             }
37364             
37365             // now build 
37366             
37367             m.render();
37368             // it's 10 on top level, and 1 on others??? why...
37369             return progressRun.defer(10, _this);
37370              
37371         }
37372         progressRun.defer(1, _this);
37373      
37374         
37375         
37376     }
37377     
37378      
37379    
37380     
37381     
37382 });
37383  //<script type="text/javascript">
37384
37385
37386 /**
37387  * @class Roo.Login
37388  * @extends Roo.LayoutDialog
37389  * A generic Login Dialog..... - only one needed in theory!?!?
37390  *
37391  * Fires XComponent builder on success...
37392  * 
37393  * Sends 
37394  *    username,password, lang = for login actions.
37395  *    check = 1 for periodic checking that sesion is valid.
37396  *    passwordRequest = email request password
37397  *    logout = 1 = to logout
37398  * 
37399  * Affects: (this id="????" elements)
37400  *   loading  (removed) (used to indicate application is loading)
37401  *   loading-mask (hides) (used to hide application when it's building loading)
37402  *   
37403  * 
37404  * Usage: 
37405  *    
37406  * 
37407  * Myapp.login = Roo.Login({
37408      url: xxxx,
37409    
37410      realm : 'Myapp', 
37411      
37412      
37413      method : 'POST',
37414      
37415      
37416      * 
37417  })
37418  * 
37419  * 
37420  * 
37421  **/
37422  
37423 Roo.Login = function(cfg)
37424 {
37425     this.addEvents({
37426         'refreshed' : true
37427     });
37428     
37429     Roo.apply(this,cfg);
37430     
37431     Roo.onReady(function() {
37432         this.onLoad();
37433     }, this);
37434     // call parent..
37435     
37436    
37437     Roo.Login.superclass.constructor.call(this, this);
37438     //this.addxtype(this.items[0]);
37439     
37440     
37441 }
37442
37443
37444 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37445     
37446     /**
37447      * @cfg {String} method
37448      * Method used to query for login details.
37449      */
37450     
37451     method : 'POST',
37452     /**
37453      * @cfg {String} url
37454      * URL to query login data. - eg. baseURL + '/Login.php'
37455      */
37456     url : '',
37457     
37458     /**
37459      * @property user
37460      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37461      * @type {Object} 
37462      */
37463     user : false,
37464     /**
37465      * @property checkFails
37466      * Number of times we have attempted to get authentication check, and failed.
37467      * @type {Number} 
37468      */
37469     checkFails : 0,
37470       /**
37471      * @property intervalID
37472      * The window interval that does the constant login checking.
37473      * @type {Number} 
37474      */
37475     intervalID : 0,
37476     
37477     
37478     onLoad : function() // called on page load...
37479     {
37480         // load 
37481          
37482         if (Roo.get('loading')) { // clear any loading indicator..
37483             Roo.get('loading').remove();
37484         }
37485         
37486         //this.switchLang('en'); // set the language to english..
37487        
37488         this.check({
37489             success:  function(response, opts)  {  // check successfull...
37490             
37491                 var res = this.processResponse(response);
37492                 this.checkFails =0;
37493                 if (!res.success) { // error!
37494                     this.checkFails = 5;
37495                     //console.log('call failure');
37496                     return this.failure(response,opts);
37497                 }
37498                 
37499                 if (!res.data.id) { // id=0 == login failure.
37500                     return this.show();
37501                 }
37502                 
37503                               
37504                         //console.log(success);
37505                 this.fillAuth(res.data);   
37506                 this.checkFails =0;
37507                 Roo.XComponent.build();
37508             },
37509             failure : this.show
37510         });
37511         
37512     }, 
37513     
37514     
37515     check: function(cfg) // called every so often to refresh cookie etc..
37516     {
37517         if (cfg.again) { // could be undefined..
37518             this.checkFails++;
37519         } else {
37520             this.checkFails = 0;
37521         }
37522         var _this = this;
37523         if (this.sending) {
37524             if ( this.checkFails > 4) {
37525                 Roo.MessageBox.alert("Error",  
37526                     "Error getting authentication status. - try reloading, or wait a while", function() {
37527                         _this.sending = false;
37528                     }); 
37529                 return;
37530             }
37531             cfg.again = true;
37532             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37533             return;
37534         }
37535         this.sending = true;
37536         
37537         Roo.Ajax.request({  
37538             url: this.url,
37539             params: {
37540                 getAuthUser: true
37541             },  
37542             method: this.method,
37543             success:  cfg.success || this.success,
37544             failure : cfg.failure || this.failure,
37545             scope : this,
37546             callCfg : cfg
37547               
37548         });  
37549     }, 
37550     
37551     
37552     logout: function()
37553     {
37554         window.onbeforeunload = function() { }; // false does not work for IE..
37555         this.user = false;
37556         var _this = this;
37557         
37558         Roo.Ajax.request({  
37559             url: this.url,
37560             params: {
37561                 logout: 1
37562             },  
37563             method: 'GET',
37564             failure : function() {
37565                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37566                     document.location = document.location.toString() + '?ts=' + Math.random();
37567                 });
37568                 
37569             },
37570             success : function() {
37571                 _this.user = false;
37572                 this.checkFails =0;
37573                 // fixme..
37574                 document.location = document.location.toString() + '?ts=' + Math.random();
37575             }
37576               
37577               
37578         }); 
37579     },
37580     
37581     processResponse : function (response)
37582     {
37583         var res = '';
37584         try {
37585             res = Roo.decode(response.responseText);
37586             // oops...
37587             if (typeof(res) != 'object') {
37588                 res = { success : false, errorMsg : res, errors : true };
37589             }
37590             if (typeof(res.success) == 'undefined') {
37591                 res.success = false;
37592             }
37593             
37594         } catch(e) {
37595             res = { success : false,  errorMsg : response.responseText, errors : true };
37596         }
37597         return res;
37598     },
37599     
37600     success : function(response, opts)  // check successfull...
37601     {  
37602         this.sending = false;
37603         var res = this.processResponse(response);
37604         if (!res.success) {
37605             return this.failure(response, opts);
37606         }
37607         if (!res.data || !res.data.id) {
37608             return this.failure(response,opts);
37609         }
37610         //console.log(res);
37611         this.fillAuth(res.data);
37612         
37613         this.checkFails =0;
37614         
37615     },
37616     
37617     
37618     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37619     {
37620         this.authUser = -1;
37621         this.sending = false;
37622         var res = this.processResponse(response);
37623         //console.log(res);
37624         if ( this.checkFails > 2) {
37625         
37626             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37627                 "Error getting authentication status. - try reloading"); 
37628             return;
37629         }
37630         opts.callCfg.again = true;
37631         this.check.defer(1000, this, [ opts.callCfg ]);
37632         return;  
37633     },
37634     
37635     
37636     
37637     fillAuth: function(au) {
37638         this.startAuthCheck();
37639         this.authUserId = au.id;
37640         this.authUser = au;
37641         this.lastChecked = new Date();
37642         this.fireEvent('refreshed', au);
37643         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37644         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37645         au.lang = au.lang || 'en';
37646         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37647         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37648         this.switchLang(au.lang );
37649         
37650      
37651         // open system... - -on setyp..
37652         if (this.authUserId  < 0) {
37653             Roo.MessageBox.alert("Warning", 
37654                 "This is an open system - please set up a admin user with a password.");  
37655         }
37656          
37657         //Pman.onload(); // which should do nothing if it's a re-auth result...
37658         
37659              
37660     },
37661     
37662     startAuthCheck : function() // starter for timeout checking..
37663     {
37664         if (this.intervalID) { // timer already in place...
37665             return false;
37666         }
37667         var _this = this;
37668         this.intervalID =  window.setInterval(function() {
37669               _this.check(false);
37670             }, 120000); // every 120 secs = 2mins..
37671         
37672         
37673     },
37674          
37675     
37676     switchLang : function (lang) 
37677     {
37678         _T = typeof(_T) == 'undefined' ? false : _T;
37679           if (!_T || !lang.length) {
37680             return;
37681         }
37682         
37683         if (!_T && lang != 'en') {
37684             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37685             return;
37686         }
37687         
37688         if (typeof(_T.en) == 'undefined') {
37689             _T.en = {};
37690             Roo.apply(_T.en, _T);
37691         }
37692         
37693         if (typeof(_T[lang]) == 'undefined') {
37694             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37695             return;
37696         }
37697         
37698         
37699         Roo.apply(_T, _T[lang]);
37700         // just need to set the text values for everything...
37701         var _this = this;
37702         /* this will not work ...
37703         if (this.form) { 
37704             
37705                
37706             function formLabel(name, val) {
37707                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37708             }
37709             
37710             formLabel('password', "Password"+':');
37711             formLabel('username', "Email Address"+':');
37712             formLabel('lang', "Language"+':');
37713             this.dialog.setTitle("Login");
37714             this.dialog.buttons[0].setText("Forgot Password");
37715             this.dialog.buttons[1].setText("Login");
37716         }
37717         */
37718         
37719         
37720     },
37721     
37722     
37723     title: "Login",
37724     modal: true,
37725     width:  350,
37726     //height: 230,
37727     height: 180,
37728     shadow: true,
37729     minWidth:200,
37730     minHeight:180,
37731     //proxyDrag: true,
37732     closable: false,
37733     draggable: false,
37734     collapsible: false,
37735     resizable: false,
37736     center: {  // needed??
37737         autoScroll:false,
37738         titlebar: false,
37739        // tabPosition: 'top',
37740         hideTabs: true,
37741         closeOnTab: true,
37742         alwaysShowTabs: false
37743     } ,
37744     listeners : {
37745         
37746         show  : function(dlg)
37747         {
37748             //console.log(this);
37749             this.form = this.layout.getRegion('center').activePanel.form;
37750             this.form.dialog = dlg;
37751             this.buttons[0].form = this.form;
37752             this.buttons[0].dialog = dlg;
37753             this.buttons[1].form = this.form;
37754             this.buttons[1].dialog = dlg;
37755            
37756            //this.resizeToLogo.defer(1000,this);
37757             // this is all related to resizing for logos..
37758             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37759            //// if (!sz) {
37760              //   this.resizeToLogo.defer(1000,this);
37761              //   return;
37762            // }
37763             //var w = Ext.lib.Dom.getViewWidth() - 100;
37764             //var h = Ext.lib.Dom.getViewHeight() - 100;
37765             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37766             //this.center();
37767             if (this.disabled) {
37768                 this.hide();
37769                 return;
37770             }
37771             
37772             if (this.user.id < 0) { // used for inital setup situations.
37773                 return;
37774             }
37775             
37776             if (this.intervalID) {
37777                 // remove the timer
37778                 window.clearInterval(this.intervalID);
37779                 this.intervalID = false;
37780             }
37781             
37782             
37783             if (Roo.get('loading')) {
37784                 Roo.get('loading').remove();
37785             }
37786             if (Roo.get('loading-mask')) {
37787                 Roo.get('loading-mask').hide();
37788             }
37789             
37790             //incomming._node = tnode;
37791             this.form.reset();
37792             //this.dialog.modal = !modal;
37793             //this.dialog.show();
37794             this.el.unmask(); 
37795             
37796             
37797             this.form.setValues({
37798                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37799                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37800             });
37801             
37802             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37803             if (this.form.findField('username').getValue().length > 0 ){
37804                 this.form.findField('password').focus();
37805             } else {
37806                this.form.findField('username').focus();
37807             }
37808     
37809         }
37810     },
37811     items : [
37812          {
37813        
37814             xtype : 'ContentPanel',
37815             xns : Roo,
37816             region: 'center',
37817             fitToFrame : true,
37818             
37819             items : [
37820     
37821                 {
37822                
37823                     xtype : 'Form',
37824                     xns : Roo.form,
37825                     labelWidth: 100,
37826                     style : 'margin: 10px;',
37827                     
37828                     listeners : {
37829                         actionfailed : function(f, act) {
37830                             // form can return { errors: .... }
37831                                 
37832                             //act.result.errors // invalid form element list...
37833                             //act.result.errorMsg// invalid form element list...
37834                             
37835                             this.dialog.el.unmask();
37836                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37837                                         "Login failed - communication error - try again.");
37838                                       
37839                         },
37840                         actioncomplete: function(re, act) {
37841                              
37842                             Roo.state.Manager.set(
37843                                 this.dialog.realm + '.username',  
37844                                     this.findField('username').getValue()
37845                             );
37846                             Roo.state.Manager.set(
37847                                 this.dialog.realm + '.lang',  
37848                                 this.findField('lang').getValue() 
37849                             );
37850                             
37851                             this.dialog.fillAuth(act.result.data);
37852                               
37853                             this.dialog.hide();
37854                             
37855                             if (Roo.get('loading-mask')) {
37856                                 Roo.get('loading-mask').show();
37857                             }
37858                             Roo.XComponent.build();
37859                             
37860                              
37861                             
37862                         }
37863                     },
37864                     items : [
37865                         {
37866                             xtype : 'TextField',
37867                             xns : Roo.form,
37868                             fieldLabel: "Email Address",
37869                             name: 'username',
37870                             width:200,
37871                             autoCreate : {tag: "input", type: "text", size: "20"}
37872                         },
37873                         {
37874                             xtype : 'TextField',
37875                             xns : Roo.form,
37876                             fieldLabel: "Password",
37877                             inputType: 'password',
37878                             name: 'password',
37879                             width:200,
37880                             autoCreate : {tag: "input", type: "text", size: "20"},
37881                             listeners : {
37882                                 specialkey : function(e,ev) {
37883                                     if (ev.keyCode == 13) {
37884                                         this.form.dialog.el.mask("Logging in");
37885                                         this.form.doAction('submit', {
37886                                             url: this.form.dialog.url,
37887                                             method: this.form.dialog.method
37888                                         });
37889                                     }
37890                                 }
37891                             }  
37892                         },
37893                         {
37894                             xtype : 'ComboBox',
37895                             xns : Roo.form,
37896                             fieldLabel: "Language",
37897                             name : 'langdisp',
37898                             store: {
37899                                 xtype : 'SimpleStore',
37900                                 fields: ['lang', 'ldisp'],
37901                                 data : [
37902                                     [ 'en', 'English' ],
37903                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37904                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37905                                 ]
37906                             },
37907                             
37908                             valueField : 'lang',
37909                             hiddenName:  'lang',
37910                             width: 200,
37911                             displayField:'ldisp',
37912                             typeAhead: false,
37913                             editable: false,
37914                             mode: 'local',
37915                             triggerAction: 'all',
37916                             emptyText:'Select a Language...',
37917                             selectOnFocus:true,
37918                             listeners : {
37919                                 select :  function(cb, rec, ix) {
37920                                     this.form.switchLang(rec.data.lang);
37921                                 }
37922                             }
37923                         
37924                         }
37925                     ]
37926                 }
37927                   
37928                 
37929             ]
37930         }
37931     ],
37932     buttons : [
37933         {
37934             xtype : 'Button',
37935             xns : 'Roo',
37936             text : "Forgot Password",
37937             listeners : {
37938                 click : function() {
37939                     //console.log(this);
37940                     var n = this.form.findField('username').getValue();
37941                     if (!n.length) {
37942                         Roo.MessageBox.alert("Error", "Fill in your email address");
37943                         return;
37944                     }
37945                     Roo.Ajax.request({
37946                         url: this.dialog.url,
37947                         params: {
37948                             passwordRequest: n
37949                         },
37950                         method: this.dialog.method,
37951                         success:  function(response, opts)  {  // check successfull...
37952                         
37953                             var res = this.dialog.processResponse(response);
37954                             if (!res.success) { // error!
37955                                Roo.MessageBox.alert("Error" ,
37956                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37957                                return;
37958                             }
37959                             Roo.MessageBox.alert("Notice" ,
37960                                 "Please check you email for the Password Reset message");
37961                         },
37962                         failure : function() {
37963                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37964                         }
37965                         
37966                     });
37967                 }
37968             }
37969         },
37970         {
37971             xtype : 'Button',
37972             xns : 'Roo',
37973             text : "Login",
37974             listeners : {
37975                 
37976                 click : function () {
37977                         
37978                     this.dialog.el.mask("Logging in");
37979                     this.form.doAction('submit', {
37980                             url: this.dialog.url,
37981                             method: this.dialog.method
37982                     });
37983                 }
37984             }
37985         }
37986     ]
37987   
37988   
37989 })
37990  
37991
37992
37993