roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
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             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @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)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <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
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674     /**
6675      * All child nodes of this node. @type Array
6676      */
6677     this.childNodes = [];
6678     if(!this.childNodes.indexOf){ // indexOf is a must
6679         this.childNodes.indexOf = function(o){
6680             for(var i = 0, len = this.length; i < len; i++){
6681                 if(this[i] == o) {
6682                     return i;
6683                 }
6684             }
6685             return -1;
6686         };
6687     }
6688     /**
6689      * The parent node for this node. @type Node
6690      */
6691     this.parentNode = null;
6692     /**
6693      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6694      */
6695     this.firstChild = null;
6696     /**
6697      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6698      */
6699     this.lastChild = null;
6700     /**
6701      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6702      */
6703     this.previousSibling = null;
6704     /**
6705      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6706      */
6707     this.nextSibling = null;
6708
6709     this.addEvents({
6710        /**
6711         * @event append
6712         * Fires when a new child node is appended
6713         * @param {Tree} tree The owner tree
6714         * @param {Node} this This node
6715         * @param {Node} node The newly appended node
6716         * @param {Number} index The index of the newly appended node
6717         */
6718        "append" : true,
6719        /**
6720         * @event remove
6721         * Fires when a child node is removed
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The removed node
6725         */
6726        "remove" : true,
6727        /**
6728         * @event move
6729         * Fires when this node is moved to a new location in the tree
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The old parent of this node
6733         * @param {Node} newParent The new parent of this node
6734         * @param {Number} index The index it was moved to
6735         */
6736        "move" : true,
6737        /**
6738         * @event insert
6739         * Fires when a new child node is inserted.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node inserted
6743         * @param {Node} refNode The child node the node was inserted before
6744         */
6745        "insert" : true,
6746        /**
6747         * @event beforeappend
6748         * Fires before a new child is appended, return false to cancel the append.
6749         * @param {Tree} tree The owner tree
6750         * @param {Node} this This node
6751         * @param {Node} node The child node to be appended
6752         */
6753        "beforeappend" : true,
6754        /**
6755         * @event beforeremove
6756         * Fires before a child is removed, return false to cancel the remove.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be removed
6760         */
6761        "beforeremove" : true,
6762        /**
6763         * @event beforemove
6764         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} oldParent The parent of this node
6768         * @param {Node} newParent The new parent this node is moving to
6769         * @param {Number} index The index it is being moved to
6770         */
6771        "beforemove" : true,
6772        /**
6773         * @event beforeinsert
6774         * Fires before a new child is inserted, return false to cancel the insert.
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The child node to be inserted
6778         * @param {Node} refNode The child node the node is being inserted before
6779         */
6780        "beforeinsert" : true
6781    });
6782     this.listeners = this.attributes.listeners;
6783     Roo.data.Node.superclass.constructor.call(this);
6784 };
6785
6786 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6787     fireEvent : function(evtName){
6788         // first do standard event for this node
6789         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6790             return false;
6791         }
6792         // then bubble it up to the tree if the event wasn't cancelled
6793         var ot = this.getOwnerTree();
6794         if(ot){
6795             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6796                 return false;
6797             }
6798         }
6799         return true;
6800     },
6801
6802     /**
6803      * Returns true if this node is a leaf
6804      * @return {Boolean}
6805      */
6806     isLeaf : function(){
6807         return this.leaf === true;
6808     },
6809
6810     // private
6811     setFirstChild : function(node){
6812         this.firstChild = node;
6813     },
6814
6815     //private
6816     setLastChild : function(node){
6817         this.lastChild = node;
6818     },
6819
6820
6821     /**
6822      * Returns true if this node is the last child of its parent
6823      * @return {Boolean}
6824      */
6825     isLast : function(){
6826        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6827     },
6828
6829     /**
6830      * Returns true if this node is the first child of its parent
6831      * @return {Boolean}
6832      */
6833     isFirst : function(){
6834        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6835     },
6836
6837     hasChildNodes : function(){
6838         return !this.isLeaf() && this.childNodes.length > 0;
6839     },
6840
6841     /**
6842      * Insert node(s) as the last child node of this node.
6843      * @param {Node/Array} node The node or Array of nodes to append
6844      * @return {Node} The appended node if single append, or null if an array was passed
6845      */
6846     appendChild : function(node){
6847         var multi = false;
6848         if(node instanceof Array){
6849             multi = node;
6850         }else if(arguments.length > 1){
6851             multi = arguments;
6852         }
6853         // if passed an array or multiple args do them one by one
6854         if(multi){
6855             for(var i = 0, len = multi.length; i < len; i++) {
6856                 this.appendChild(multi[i]);
6857             }
6858         }else{
6859             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6860                 return false;
6861             }
6862             var index = this.childNodes.length;
6863             var oldParent = node.parentNode;
6864             // it's a move, make sure we move it cleanly
6865             if(oldParent){
6866                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6867                     return false;
6868                 }
6869                 oldParent.removeChild(node);
6870             }
6871             index = this.childNodes.length;
6872             if(index == 0){
6873                 this.setFirstChild(node);
6874             }
6875             this.childNodes.push(node);
6876             node.parentNode = this;
6877             var ps = this.childNodes[index-1];
6878             if(ps){
6879                 node.previousSibling = ps;
6880                 ps.nextSibling = node;
6881             }else{
6882                 node.previousSibling = null;
6883             }
6884             node.nextSibling = null;
6885             this.setLastChild(node);
6886             node.setOwnerTree(this.getOwnerTree());
6887             this.fireEvent("append", this.ownerTree, this, node, index);
6888             if(oldParent){
6889                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6890             }
6891             return node;
6892         }
6893     },
6894
6895     /**
6896      * Removes a child node from this node.
6897      * @param {Node} node The node to remove
6898      * @return {Node} The removed node
6899      */
6900     removeChild : function(node){
6901         var index = this.childNodes.indexOf(node);
6902         if(index == -1){
6903             return false;
6904         }
6905         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6906             return false;
6907         }
6908
6909         // remove it from childNodes collection
6910         this.childNodes.splice(index, 1);
6911
6912         // update siblings
6913         if(node.previousSibling){
6914             node.previousSibling.nextSibling = node.nextSibling;
6915         }
6916         if(node.nextSibling){
6917             node.nextSibling.previousSibling = node.previousSibling;
6918         }
6919
6920         // update child refs
6921         if(this.firstChild == node){
6922             this.setFirstChild(node.nextSibling);
6923         }
6924         if(this.lastChild == node){
6925             this.setLastChild(node.previousSibling);
6926         }
6927
6928         node.setOwnerTree(null);
6929         // clear any references from the node
6930         node.parentNode = null;
6931         node.previousSibling = null;
6932         node.nextSibling = null;
6933         this.fireEvent("remove", this.ownerTree, this, node);
6934         return node;
6935     },
6936
6937     /**
6938      * Inserts the first node before the second node in this nodes childNodes collection.
6939      * @param {Node} node The node to insert
6940      * @param {Node} refNode The node to insert before (if null the node is appended)
6941      * @return {Node} The inserted node
6942      */
6943     insertBefore : function(node, refNode){
6944         if(!refNode){ // like standard Dom, refNode can be null for append
6945             return this.appendChild(node);
6946         }
6947         // nothing to do
6948         if(node == refNode){
6949             return false;
6950         }
6951
6952         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6953             return false;
6954         }
6955         var index = this.childNodes.indexOf(refNode);
6956         var oldParent = node.parentNode;
6957         var refIndex = index;
6958
6959         // when moving internally, indexes will change after remove
6960         if(oldParent == this && this.childNodes.indexOf(node) < index){
6961             refIndex--;
6962         }
6963
6964         // it's a move, make sure we move it cleanly
6965         if(oldParent){
6966             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6967                 return false;
6968             }
6969             oldParent.removeChild(node);
6970         }
6971         if(refIndex == 0){
6972             this.setFirstChild(node);
6973         }
6974         this.childNodes.splice(refIndex, 0, node);
6975         node.parentNode = this;
6976         var ps = this.childNodes[refIndex-1];
6977         if(ps){
6978             node.previousSibling = ps;
6979             ps.nextSibling = node;
6980         }else{
6981             node.previousSibling = null;
6982         }
6983         node.nextSibling = refNode;
6984         refNode.previousSibling = node;
6985         node.setOwnerTree(this.getOwnerTree());
6986         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6987         if(oldParent){
6988             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6989         }
6990         return node;
6991     },
6992
6993     /**
6994      * Returns the child node at the specified index.
6995      * @param {Number} index
6996      * @return {Node}
6997      */
6998     item : function(index){
6999         return this.childNodes[index];
7000     },
7001
7002     /**
7003      * Replaces one child node in this node with another.
7004      * @param {Node} newChild The replacement node
7005      * @param {Node} oldChild The node to replace
7006      * @return {Node} The replaced node
7007      */
7008     replaceChild : function(newChild, oldChild){
7009         this.insertBefore(newChild, oldChild);
7010         this.removeChild(oldChild);
7011         return oldChild;
7012     },
7013
7014     /**
7015      * Returns the index of a child node
7016      * @param {Node} node
7017      * @return {Number} The index of the node or -1 if it was not found
7018      */
7019     indexOf : function(child){
7020         return this.childNodes.indexOf(child);
7021     },
7022
7023     /**
7024      * Returns the tree this node is in.
7025      * @return {Tree}
7026      */
7027     getOwnerTree : function(){
7028         // if it doesn't have one, look for one
7029         if(!this.ownerTree){
7030             var p = this;
7031             while(p){
7032                 if(p.ownerTree){
7033                     this.ownerTree = p.ownerTree;
7034                     break;
7035                 }
7036                 p = p.parentNode;
7037             }
7038         }
7039         return this.ownerTree;
7040     },
7041
7042     /**
7043      * Returns depth of this node (the root node has a depth of 0)
7044      * @return {Number}
7045      */
7046     getDepth : function(){
7047         var depth = 0;
7048         var p = this;
7049         while(p.parentNode){
7050             ++depth;
7051             p = p.parentNode;
7052         }
7053         return depth;
7054     },
7055
7056     // private
7057     setOwnerTree : function(tree){
7058         // if it's move, we need to update everyone
7059         if(tree != this.ownerTree){
7060             if(this.ownerTree){
7061                 this.ownerTree.unregisterNode(this);
7062             }
7063             this.ownerTree = tree;
7064             var cs = this.childNodes;
7065             for(var i = 0, len = cs.length; i < len; i++) {
7066                 cs[i].setOwnerTree(tree);
7067             }
7068             if(tree){
7069                 tree.registerNode(this);
7070             }
7071         }
7072     },
7073
7074     /**
7075      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7076      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7077      * @return {String} The path
7078      */
7079     getPath : function(attr){
7080         attr = attr || "id";
7081         var p = this.parentNode;
7082         var b = [this.attributes[attr]];
7083         while(p){
7084             b.unshift(p.attributes[attr]);
7085             p = p.parentNode;
7086         }
7087         var sep = this.getOwnerTree().pathSeparator;
7088         return sep + b.join(sep);
7089     },
7090
7091     /**
7092      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the bubble is stopped.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     bubble : function(fn, scope, args){
7101         var p = this;
7102         while(p){
7103             if(fn.call(scope || p, args || p) === false){
7104                 break;
7105             }
7106             p = p.parentNode;
7107         }
7108     },
7109
7110     /**
7111      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7112      * function call will be the scope provided or the current node. The arguments to the function
7113      * will be the args provided or the current node. If the function returns false at any point,
7114      * the cascade is stopped on that branch.
7115      * @param {Function} fn The function to call
7116      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7117      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7118      */
7119     cascade : function(fn, scope, args){
7120         if(fn.call(scope || this, args || this) !== false){
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].cascade(fn, scope, args);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7130      * function call will be the scope provided or the current node. The arguments to the function
7131      * will be the args provided or the current node. If the function returns false at any point,
7132      * the iteration stops.
7133      * @param {Function} fn The function to call
7134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7136      */
7137     eachChild : function(fn, scope, args){
7138         var cs = this.childNodes;
7139         for(var i = 0, len = cs.length; i < len; i++) {
7140                 if(fn.call(scope || this, args || cs[i]) === false){
7141                     break;
7142                 }
7143         }
7144     },
7145
7146     /**
7147      * Finds the first child that has the attribute with the specified value.
7148      * @param {String} attribute The attribute name
7149      * @param {Mixed} value The value to search for
7150      * @return {Node} The found child or null if none was found
7151      */
7152     findChild : function(attribute, value){
7153         var cs = this.childNodes;
7154         for(var i = 0, len = cs.length; i < len; i++) {
7155                 if(cs[i].attributes[attribute] == value){
7156                     return cs[i];
7157                 }
7158         }
7159         return null;
7160     },
7161
7162     /**
7163      * Finds the first child by a custom function. The child matches if the function passed
7164      * returns true.
7165      * @param {Function} fn
7166      * @param {Object} scope (optional)
7167      * @return {Node} The found child or null if none was found
7168      */
7169     findChildBy : function(fn, scope){
7170         var cs = this.childNodes;
7171         for(var i = 0, len = cs.length; i < len; i++) {
7172                 if(fn.call(scope||cs[i], cs[i]) === true){
7173                     return cs[i];
7174                 }
7175         }
7176         return null;
7177     },
7178
7179     /**
7180      * Sorts this nodes children using the supplied sort function
7181      * @param {Function} fn
7182      * @param {Object} scope (optional)
7183      */
7184     sort : function(fn, scope){
7185         var cs = this.childNodes;
7186         var len = cs.length;
7187         if(len > 0){
7188             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7189             cs.sort(sortFn);
7190             for(var i = 0; i < len; i++){
7191                 var n = cs[i];
7192                 n.previousSibling = cs[i-1];
7193                 n.nextSibling = cs[i+1];
7194                 if(i == 0){
7195                     this.setFirstChild(n);
7196                 }
7197                 if(i == len-1){
7198                     this.setLastChild(n);
7199                 }
7200             }
7201         }
7202     },
7203
7204     /**
7205      * Returns true if this node is an ancestor (at any point) of the passed node.
7206      * @param {Node} node
7207      * @return {Boolean}
7208      */
7209     contains : function(node){
7210         return node.isAncestor(this);
7211     },
7212
7213     /**
7214      * Returns true if the passed node is an ancestor (at any point) of this node.
7215      * @param {Node} node
7216      * @return {Boolean}
7217      */
7218     isAncestor : function(node){
7219         var p = this.parentNode;
7220         while(p){
7221             if(p == node){
7222                 return true;
7223             }
7224             p = p.parentNode;
7225         }
7226         return false;
7227     },
7228
7229     toString : function(){
7230         return "[Node"+(this.id?" "+this.id:"")+"]";
7231     }
7232 });/*
7233  * Based on:
7234  * Ext JS Library 1.1.1
7235  * Copyright(c) 2006-2007, Ext JS, LLC.
7236  *
7237  * Originally Released Under LGPL - original licence link has changed is not relivant.
7238  *
7239  * Fork - LGPL
7240  * <script type="text/javascript">
7241  */
7242  
7243
7244 /**
7245  * @class Roo.ComponentMgr
7246  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7247  * @singleton
7248  */
7249 Roo.ComponentMgr = function(){
7250     var all = new Roo.util.MixedCollection();
7251
7252     return {
7253         /**
7254          * Registers a component.
7255          * @param {Roo.Component} c The component
7256          */
7257         register : function(c){
7258             all.add(c);
7259         },
7260
7261         /**
7262          * Unregisters a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         unregister : function(c){
7266             all.remove(c);
7267         },
7268
7269         /**
7270          * Returns a component by id
7271          * @param {String} id The component id
7272          */
7273         get : function(id){
7274             return all.get(id);
7275         },
7276
7277         /**
7278          * Registers a function that will be called when a specified component is added to ComponentMgr
7279          * @param {String} id The component id
7280          * @param {Funtction} fn The callback function
7281          * @param {Object} scope The scope of the callback
7282          */
7283         onAvailable : function(id, fn, scope){
7284             all.on("add", function(index, o){
7285                 if(o.id == id){
7286                     fn.call(scope || o, o);
7287                     all.un("add", fn, scope);
7288                 }
7289             });
7290         }
7291     };
7292 }();/*
7293  * Based on:
7294  * Ext JS Library 1.1.1
7295  * Copyright(c) 2006-2007, Ext JS, LLC.
7296  *
7297  * Originally Released Under LGPL - original licence link has changed is not relivant.
7298  *
7299  * Fork - LGPL
7300  * <script type="text/javascript">
7301  */
7302  
7303 /**
7304  * @class Roo.Component
7305  * @extends Roo.util.Observable
7306  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7307  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7308  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7309  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7310  * All visual components (widgets) that require rendering into a layout should subclass Component.
7311  * @constructor
7312  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7313  * 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
7314  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7315  */
7316 Roo.Component = function(config){
7317     config = config || {};
7318     if(config.tagName || config.dom || typeof config == "string"){ // element object
7319         config = {el: config, id: config.id || config};
7320     }
7321     this.initialConfig = config;
7322
7323     Roo.apply(this, config);
7324     this.addEvents({
7325         /**
7326          * @event disable
7327          * Fires after the component is disabled.
7328              * @param {Roo.Component} this
7329              */
7330         disable : true,
7331         /**
7332          * @event enable
7333          * Fires after the component is enabled.
7334              * @param {Roo.Component} this
7335              */
7336         enable : true,
7337         /**
7338          * @event beforeshow
7339          * Fires before the component is shown.  Return false to stop the show.
7340              * @param {Roo.Component} this
7341              */
7342         beforeshow : true,
7343         /**
7344          * @event show
7345          * Fires after the component is shown.
7346              * @param {Roo.Component} this
7347              */
7348         show : true,
7349         /**
7350          * @event beforehide
7351          * Fires before the component is hidden. Return false to stop the hide.
7352              * @param {Roo.Component} this
7353              */
7354         beforehide : true,
7355         /**
7356          * @event hide
7357          * Fires after the component is hidden.
7358              * @param {Roo.Component} this
7359              */
7360         hide : true,
7361         /**
7362          * @event beforerender
7363          * Fires before the component is rendered. Return false to stop the render.
7364              * @param {Roo.Component} this
7365              */
7366         beforerender : true,
7367         /**
7368          * @event render
7369          * Fires after the component is rendered.
7370              * @param {Roo.Component} this
7371              */
7372         render : true,
7373         /**
7374          * @event beforedestroy
7375          * Fires before the component is destroyed. Return false to stop the destroy.
7376              * @param {Roo.Component} this
7377              */
7378         beforedestroy : true,
7379         /**
7380          * @event destroy
7381          * Fires after the component is destroyed.
7382              * @param {Roo.Component} this
7383              */
7384         destroy : true
7385     });
7386     if(!this.id){
7387         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7388     }
7389     Roo.ComponentMgr.register(this);
7390     Roo.Component.superclass.constructor.call(this);
7391     this.initComponent();
7392     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7393         this.render(this.renderTo);
7394         delete this.renderTo;
7395     }
7396 };
7397
7398 /** @private */
7399 Roo.Component.AUTO_ID = 1000;
7400
7401 Roo.extend(Roo.Component, Roo.util.Observable, {
7402     /**
7403      * @scope Roo.Component.prototype
7404      * @type {Boolean}
7405      * true if this component is hidden. Read-only.
7406      */
7407     hidden : false,
7408     /**
7409      * @type {Boolean}
7410      * true if this component is disabled. Read-only.
7411      */
7412     disabled : false,
7413     /**
7414      * @type {Boolean}
7415      * true if this component has been rendered. Read-only.
7416      */
7417     rendered : false,
7418     
7419     /** @cfg {String} disableClass
7420      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7421      */
7422     disabledClass : "x-item-disabled",
7423         /** @cfg {Boolean} allowDomMove
7424          * Whether the component can move the Dom node when rendering (defaults to true).
7425          */
7426     allowDomMove : true,
7427     /** @cfg {String} hideMode
7428      * How this component should hidden. Supported values are
7429      * "visibility" (css visibility), "offsets" (negative offset position) and
7430      * "display" (css display) - defaults to "display".
7431      */
7432     hideMode: 'display',
7433
7434     /** @private */
7435     ctype : "Roo.Component",
7436
7437     /**
7438      * @cfg {String} actionMode 
7439      * which property holds the element that used for  hide() / show() / disable() / enable()
7440      * default is 'el' 
7441      */
7442     actionMode : "el",
7443
7444     /** @private */
7445     getActionEl : function(){
7446         return this[this.actionMode];
7447     },
7448
7449     initComponent : Roo.emptyFn,
7450     /**
7451      * If this is a lazy rendering component, render it to its container element.
7452      * @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.
7453      */
7454     render : function(container, position){
7455         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7456             if(!container && this.el){
7457                 this.el = Roo.get(this.el);
7458                 container = this.el.dom.parentNode;
7459                 this.allowDomMove = false;
7460             }
7461             this.container = Roo.get(container);
7462             this.rendered = true;
7463             if(position !== undefined){
7464                 if(typeof position == 'number'){
7465                     position = this.container.dom.childNodes[position];
7466                 }else{
7467                     position = Roo.getDom(position);
7468                 }
7469             }
7470             this.onRender(this.container, position || null);
7471             if(this.cls){
7472                 this.el.addClass(this.cls);
7473                 delete this.cls;
7474             }
7475             if(this.style){
7476                 this.el.applyStyles(this.style);
7477                 delete this.style;
7478             }
7479             this.fireEvent("render", this);
7480             this.afterRender(this.container);
7481             if(this.hidden){
7482                 this.hide();
7483             }
7484             if(this.disabled){
7485                 this.disable();
7486             }
7487         }
7488         return this;
7489     },
7490
7491     /** @private */
7492     // default function is not really useful
7493     onRender : function(ct, position){
7494         if(this.el){
7495             this.el = Roo.get(this.el);
7496             if(this.allowDomMove !== false){
7497                 ct.dom.insertBefore(this.el.dom, position);
7498             }
7499         }
7500     },
7501
7502     /** @private */
7503     getAutoCreate : function(){
7504         var cfg = typeof this.autoCreate == "object" ?
7505                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7506         if(this.id && !cfg.id){
7507             cfg.id = this.id;
7508         }
7509         return cfg;
7510     },
7511
7512     /** @private */
7513     afterRender : Roo.emptyFn,
7514
7515     /**
7516      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7517      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7518      */
7519     destroy : function(){
7520         if(this.fireEvent("beforedestroy", this) !== false){
7521             this.purgeListeners();
7522             this.beforeDestroy();
7523             if(this.rendered){
7524                 this.el.removeAllListeners();
7525                 this.el.remove();
7526                 if(this.actionMode == "container"){
7527                     this.container.remove();
7528                 }
7529             }
7530             this.onDestroy();
7531             Roo.ComponentMgr.unregister(this);
7532             this.fireEvent("destroy", this);
7533         }
7534     },
7535
7536         /** @private */
7537     beforeDestroy : function(){
7538
7539     },
7540
7541         /** @private */
7542         onDestroy : function(){
7543
7544     },
7545
7546     /**
7547      * Returns the underlying {@link Roo.Element}.
7548      * @return {Roo.Element} The element
7549      */
7550     getEl : function(){
7551         return this.el;
7552     },
7553
7554     /**
7555      * Returns the id of this component.
7556      * @return {String}
7557      */
7558     getId : function(){
7559         return this.id;
7560     },
7561
7562     /**
7563      * Try to focus this component.
7564      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7565      * @return {Roo.Component} this
7566      */
7567     focus : function(selectText){
7568         if(this.rendered){
7569             this.el.focus();
7570             if(selectText === true){
7571                 this.el.dom.select();
7572             }
7573         }
7574         return this;
7575     },
7576
7577     /** @private */
7578     blur : function(){
7579         if(this.rendered){
7580             this.el.blur();
7581         }
7582         return this;
7583     },
7584
7585     /**
7586      * Disable this component.
7587      * @return {Roo.Component} this
7588      */
7589     disable : function(){
7590         if(this.rendered){
7591             this.onDisable();
7592         }
7593         this.disabled = true;
7594         this.fireEvent("disable", this);
7595         return this;
7596     },
7597
7598         // private
7599     onDisable : function(){
7600         this.getActionEl().addClass(this.disabledClass);
7601         this.el.dom.disabled = true;
7602     },
7603
7604     /**
7605      * Enable this component.
7606      * @return {Roo.Component} this
7607      */
7608     enable : function(){
7609         if(this.rendered){
7610             this.onEnable();
7611         }
7612         this.disabled = false;
7613         this.fireEvent("enable", this);
7614         return this;
7615     },
7616
7617         // private
7618     onEnable : function(){
7619         this.getActionEl().removeClass(this.disabledClass);
7620         this.el.dom.disabled = false;
7621     },
7622
7623     /**
7624      * Convenience function for setting disabled/enabled by boolean.
7625      * @param {Boolean} disabled
7626      */
7627     setDisabled : function(disabled){
7628         this[disabled ? "disable" : "enable"]();
7629     },
7630
7631     /**
7632      * Show this component.
7633      * @return {Roo.Component} this
7634      */
7635     show: function(){
7636         if(this.fireEvent("beforeshow", this) !== false){
7637             this.hidden = false;
7638             if(this.rendered){
7639                 this.onShow();
7640             }
7641             this.fireEvent("show", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onShow : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "visible";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.removeClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "";
7655         }
7656     },
7657
7658     /**
7659      * Hide this component.
7660      * @return {Roo.Component} this
7661      */
7662     hide: function(){
7663         if(this.fireEvent("beforehide", this) !== false){
7664             this.hidden = true;
7665             if(this.rendered){
7666                 this.onHide();
7667             }
7668             this.fireEvent("hide", this);
7669         }
7670         return this;
7671     },
7672
7673     // private
7674     onHide : function(){
7675         var ae = this.getActionEl();
7676         if(this.hideMode == 'visibility'){
7677             ae.dom.style.visibility = "hidden";
7678         }else if(this.hideMode == 'offsets'){
7679             ae.addClass('x-hidden');
7680         }else{
7681             ae.dom.style.display = "none";
7682         }
7683     },
7684
7685     /**
7686      * Convenience function to hide or show this component by boolean.
7687      * @param {Boolean} visible True to show, false to hide
7688      * @return {Roo.Component} this
7689      */
7690     setVisible: function(visible){
7691         if(visible) {
7692             this.show();
7693         }else{
7694             this.hide();
7695         }
7696         return this;
7697     },
7698
7699     /**
7700      * Returns true if this component is visible.
7701      */
7702     isVisible : function(){
7703         return this.getActionEl().isVisible();
7704     },
7705
7706     cloneConfig : function(overrides){
7707         overrides = overrides || {};
7708         var id = overrides.id || Roo.id();
7709         var cfg = Roo.applyIf(overrides, this.initialConfig);
7710         cfg.id = id; // prevent dup id
7711         return new this.constructor(cfg);
7712     }
7713 });/*
7714  * Based on:
7715  * Ext JS Library 1.1.1
7716  * Copyright(c) 2006-2007, Ext JS, LLC.
7717  *
7718  * Originally Released Under LGPL - original licence link has changed is not relivant.
7719  *
7720  * Fork - LGPL
7721  * <script type="text/javascript">
7722  */
7723  (function(){ 
7724 /**
7725  * @class Roo.Layer
7726  * @extends Roo.Element
7727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7728  * automatic maintaining of shadow/shim positions.
7729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7731  * you can pass a string with a CSS class name. False turns off the shadow.
7732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7734  * @cfg {String} cls CSS class to add to the element
7735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7737  * @constructor
7738  * @param {Object} config An object with config options.
7739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7740  */
7741
7742 Roo.Layer = function(config, existingEl){
7743     config = config || {};
7744     var dh = Roo.DomHelper;
7745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7746     if(existingEl){
7747         this.dom = Roo.getDom(existingEl);
7748     }
7749     if(!this.dom){
7750         var o = config.dh || {tag: "div", cls: "x-layer"};
7751         this.dom = dh.append(pel, o);
7752     }
7753     if(config.cls){
7754         this.addClass(config.cls);
7755     }
7756     this.constrain = config.constrain !== false;
7757     this.visibilityMode = Roo.Element.VISIBILITY;
7758     if(config.id){
7759         this.id = this.dom.id = config.id;
7760     }else{
7761         this.id = Roo.id(this.dom);
7762     }
7763     this.zindex = config.zindex || this.getZIndex();
7764     this.position("absolute", this.zindex);
7765     if(config.shadow){
7766         this.shadowOffset = config.shadowOffset || 4;
7767         this.shadow = new Roo.Shadow({
7768             offset : this.shadowOffset,
7769             mode : config.shadow
7770         });
7771     }else{
7772         this.shadowOffset = 0;
7773     }
7774     this.useShim = config.shim !== false && Roo.useShims;
7775     this.useDisplay = config.useDisplay;
7776     this.hide();
7777 };
7778
7779 var supr = Roo.Element.prototype;
7780
7781 // shims are shared among layer to keep from having 100 iframes
7782 var shims = [];
7783
7784 Roo.extend(Roo.Layer, Roo.Element, {
7785
7786     getZIndex : function(){
7787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7788     },
7789
7790     getShim : function(){
7791         if(!this.useShim){
7792             return null;
7793         }
7794         if(this.shim){
7795             return this.shim;
7796         }
7797         var shim = shims.shift();
7798         if(!shim){
7799             shim = this.createShim();
7800             shim.enableDisplayMode('block');
7801             shim.dom.style.display = 'none';
7802             shim.dom.style.visibility = 'visible';
7803         }
7804         var pn = this.dom.parentNode;
7805         if(shim.dom.parentNode != pn){
7806             pn.insertBefore(shim.dom, this.dom);
7807         }
7808         shim.setStyle('z-index', this.getZIndex()-2);
7809         this.shim = shim;
7810         return shim;
7811     },
7812
7813     hideShim : function(){
7814         if(this.shim){
7815             this.shim.setDisplayed(false);
7816             shims.push(this.shim);
7817             delete this.shim;
7818         }
7819     },
7820
7821     disableShadow : function(){
7822         if(this.shadow){
7823             this.shadowDisabled = true;
7824             this.shadow.hide();
7825             this.lastShadowOffset = this.shadowOffset;
7826             this.shadowOffset = 0;
7827         }
7828     },
7829
7830     enableShadow : function(show){
7831         if(this.shadow){
7832             this.shadowDisabled = false;
7833             this.shadowOffset = this.lastShadowOffset;
7834             delete this.lastShadowOffset;
7835             if(show){
7836                 this.sync(true);
7837             }
7838         }
7839     },
7840
7841     // private
7842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7844     sync : function(doShow){
7845         var sw = this.shadow;
7846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7847             var sh = this.getShim();
7848
7849             var w = this.getWidth(),
7850                 h = this.getHeight();
7851
7852             var l = this.getLeft(true),
7853                 t = this.getTop(true);
7854
7855             if(sw && !this.shadowDisabled){
7856                 if(doShow && !sw.isVisible()){
7857                     sw.show(this);
7858                 }else{
7859                     sw.realign(l, t, w, h);
7860                 }
7861                 if(sh){
7862                     if(doShow){
7863                        sh.show();
7864                     }
7865                     // fit the shim behind the shadow, so it is shimmed too
7866                     var a = sw.adjusts, s = sh.dom.style;
7867                     s.left = (Math.min(l, l+a.l))+"px";
7868                     s.top = (Math.min(t, t+a.t))+"px";
7869                     s.width = (w+a.w)+"px";
7870                     s.height = (h+a.h)+"px";
7871                 }
7872             }else if(sh){
7873                 if(doShow){
7874                    sh.show();
7875                 }
7876                 sh.setSize(w, h);
7877                 sh.setLeftTop(l, t);
7878             }
7879             
7880         }
7881     },
7882
7883     // private
7884     destroy : function(){
7885         this.hideShim();
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.removeAllListeners();
7890         var pn = this.dom.parentNode;
7891         if(pn){
7892             pn.removeChild(this.dom);
7893         }
7894         Roo.Element.uncache(this.id);
7895     },
7896
7897     remove : function(){
7898         this.destroy();
7899     },
7900
7901     // private
7902     beginUpdate : function(){
7903         this.updating = true;
7904     },
7905
7906     // private
7907     endUpdate : function(){
7908         this.updating = false;
7909         this.sync(true);
7910     },
7911
7912     // private
7913     hideUnders : function(negOffset){
7914         if(this.shadow){
7915             this.shadow.hide();
7916         }
7917         this.hideShim();
7918     },
7919
7920     // private
7921     constrainXY : function(){
7922         if(this.constrain){
7923             var vw = Roo.lib.Dom.getViewWidth(),
7924                 vh = Roo.lib.Dom.getViewHeight();
7925             var s = Roo.get(document).getScroll();
7926
7927             var xy = this.getXY();
7928             var x = xy[0], y = xy[1];   
7929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7930             // only move it if it needs it
7931             var moved = false;
7932             // first validate right/bottom
7933             if((x + w) > vw+s.left){
7934                 x = vw - w - this.shadowOffset;
7935                 moved = true;
7936             }
7937             if((y + h) > vh+s.top){
7938                 y = vh - h - this.shadowOffset;
7939                 moved = true;
7940             }
7941             // then make sure top/left isn't negative
7942             if(x < s.left){
7943                 x = s.left;
7944                 moved = true;
7945             }
7946             if(y < s.top){
7947                 y = s.top;
7948                 moved = true;
7949             }
7950             if(moved){
7951                 if(this.avoidY){
7952                     var ay = this.avoidY;
7953                     if(y <= ay && (y+h) >= ay){
7954                         y = ay-h-5;   
7955                     }
7956                 }
7957                 xy = [x, y];
7958                 this.storeXY(xy);
7959                 supr.setXY.call(this, xy);
7960                 this.sync();
7961             }
7962         }
7963     },
7964
7965     isVisible : function(){
7966         return this.visible;    
7967     },
7968
7969     // private
7970     showAction : function(){
7971         this.visible = true; // track visibility to prevent getStyle calls
7972         if(this.useDisplay === true){
7973             this.setDisplayed("");
7974         }else if(this.lastXY){
7975             supr.setXY.call(this, this.lastXY);
7976         }else if(this.lastLT){
7977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7978         }
7979     },
7980
7981     // private
7982     hideAction : function(){
7983         this.visible = false;
7984         if(this.useDisplay === true){
7985             this.setDisplayed(false);
7986         }else{
7987             this.setLeftTop(-10000,-10000);
7988         }
7989     },
7990
7991     // overridden Element method
7992     setVisible : function(v, a, d, c, e){
7993         if(v){
7994             this.showAction();
7995         }
7996         if(a && v){
7997             var cb = function(){
7998                 this.sync(true);
7999                 if(c){
8000                     c();
8001                 }
8002             }.createDelegate(this);
8003             supr.setVisible.call(this, true, true, d, cb, e);
8004         }else{
8005             if(!v){
8006                 this.hideUnders(true);
8007             }
8008             var cb = c;
8009             if(a){
8010                 cb = function(){
8011                     this.hideAction();
8012                     if(c){
8013                         c();
8014                     }
8015                 }.createDelegate(this);
8016             }
8017             supr.setVisible.call(this, v, a, d, cb, e);
8018             if(v){
8019                 this.sync(true);
8020             }else if(!a){
8021                 this.hideAction();
8022             }
8023         }
8024     },
8025
8026     storeXY : function(xy){
8027         delete this.lastLT;
8028         this.lastXY = xy;
8029     },
8030
8031     storeLeftTop : function(left, top){
8032         delete this.lastXY;
8033         this.lastLT = [left, top];
8034     },
8035
8036     // private
8037     beforeFx : function(){
8038         this.beforeAction();
8039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8040     },
8041
8042     // private
8043     afterFx : function(){
8044         Roo.Layer.superclass.afterFx.apply(this, arguments);
8045         this.sync(this.isVisible());
8046     },
8047
8048     // private
8049     beforeAction : function(){
8050         if(!this.updating && this.shadow){
8051             this.shadow.hide();
8052         }
8053     },
8054
8055     // overridden Element method
8056     setLeft : function(left){
8057         this.storeLeftTop(left, this.getTop(true));
8058         supr.setLeft.apply(this, arguments);
8059         this.sync();
8060     },
8061
8062     setTop : function(top){
8063         this.storeLeftTop(this.getLeft(true), top);
8064         supr.setTop.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setLeftTop : function(left, top){
8069         this.storeLeftTop(left, top);
8070         supr.setLeftTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setXY : function(xy, a, d, c, e){
8075         this.fixDisplay();
8076         this.beforeAction();
8077         this.storeXY(xy);
8078         var cb = this.createCB(c);
8079         supr.setXY.call(this, xy, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // private
8086     createCB : function(c){
8087         var el = this;
8088         return function(){
8089             el.constrainXY();
8090             el.sync(true);
8091             if(c){
8092                 c();
8093             }
8094         };
8095     },
8096
8097     // overridden Element method
8098     setX : function(x, a, d, c, e){
8099         this.setXY([x, this.getY()], a, d, c, e);
8100     },
8101
8102     // overridden Element method
8103     setY : function(y, a, d, c, e){
8104         this.setXY([this.getX(), y], a, d, c, e);
8105     },
8106
8107     // overridden Element method
8108     setSize : function(w, h, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setSize.call(this, w, h, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setWidth : function(w, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setWidth.call(this, w, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setHeight : function(h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         supr.setHeight.call(this, h, a, d, cb, e);
8132         if(!a){
8133             cb();
8134         }
8135     },
8136
8137     // overridden Element method
8138     setBounds : function(x, y, w, h, a, d, c, e){
8139         this.beforeAction();
8140         var cb = this.createCB(c);
8141         if(!a){
8142             this.storeXY([x, y]);
8143             supr.setXY.call(this, [x, y]);
8144             supr.setSize.call(this, w, h, a, d, cb, e);
8145             cb();
8146         }else{
8147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8148         }
8149         return this;
8150     },
8151     
8152     /**
8153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8156      * @param {Number} zindex The new z-index to set
8157      * @return {this} The Layer
8158      */
8159     setZIndex : function(zindex){
8160         this.zindex = zindex;
8161         this.setStyle("z-index", zindex + 2);
8162         if(this.shadow){
8163             this.shadow.setZIndex(zindex + 1);
8164         }
8165         if(this.shim){
8166             this.shim.setStyle("z-index", zindex);
8167         }
8168     }
8169 });
8170 })();/*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180
8181
8182 /**
8183  * @class Roo.Shadow
8184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8187  * @constructor
8188  * Create a new Shadow
8189  * @param {Object} config The config object
8190  */
8191 Roo.Shadow = function(config){
8192     Roo.apply(this, config);
8193     if(typeof this.mode != "string"){
8194         this.mode = this.defaultMode;
8195     }
8196     var o = this.offset, a = {h: 0};
8197     var rad = Math.floor(this.offset/2);
8198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8199         case "drop":
8200             a.w = 0;
8201             a.l = a.t = o;
8202             a.t -= 1;
8203             if(Roo.isIE){
8204                 a.l -= this.offset + rad;
8205                 a.t -= this.offset + rad;
8206                 a.w -= rad;
8207                 a.h -= rad;
8208                 a.t += 1;
8209             }
8210         break;
8211         case "sides":
8212             a.w = (o*2);
8213             a.l = -o;
8214             a.t = o-1;
8215             if(Roo.isIE){
8216                 a.l -= (this.offset - rad);
8217                 a.t -= this.offset + rad;
8218                 a.l += 1;
8219                 a.w -= (this.offset - rad)*2;
8220                 a.w -= rad + 1;
8221                 a.h -= 1;
8222             }
8223         break;
8224         case "frame":
8225             a.w = a.h = (o*2);
8226             a.l = a.t = -o;
8227             a.t += 1;
8228             a.h -= 2;
8229             if(Roo.isIE){
8230                 a.l -= (this.offset - rad);
8231                 a.t -= (this.offset - rad);
8232                 a.l += 1;
8233                 a.w -= (this.offset + rad + 1);
8234                 a.h -= (this.offset + rad);
8235                 a.h += 1;
8236             }
8237         break;
8238     };
8239
8240     this.adjusts = a;
8241 };
8242
8243 Roo.Shadow.prototype = {
8244     /**
8245      * @cfg {String} mode
8246      * The shadow display mode.  Supports the following options:<br />
8247      * sides: Shadow displays on both sides and bottom only<br />
8248      * frame: Shadow displays equally on all four sides<br />
8249      * drop: Traditional bottom-right drop shadow (default)
8250      */
8251     /**
8252      * @cfg {String} offset
8253      * The number of pixels to offset the shadow from the element (defaults to 4)
8254      */
8255     offset: 4,
8256
8257     // private
8258     defaultMode: "drop",
8259
8260     /**
8261      * Displays the shadow under the target element
8262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8263      */
8264     show : function(target){
8265         target = Roo.get(target);
8266         if(!this.el){
8267             this.el = Roo.Shadow.Pool.pull();
8268             if(this.el.dom.nextSibling != target.dom){
8269                 this.el.insertBefore(target);
8270             }
8271         }
8272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8273         if(Roo.isIE){
8274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8275         }
8276         this.realign(
8277             target.getLeft(true),
8278             target.getTop(true),
8279             target.getWidth(),
8280             target.getHeight()
8281         );
8282         this.el.dom.style.display = "block";
8283     },
8284
8285     /**
8286      * Returns true if the shadow is visible, else false
8287      */
8288     isVisible : function(){
8289         return this.el ? true : false;  
8290     },
8291
8292     /**
8293      * Direct alignment when values are already available. Show must be called at least once before
8294      * calling this method to ensure it is initialized.
8295      * @param {Number} left The target element left position
8296      * @param {Number} top The target element top position
8297      * @param {Number} width The target element width
8298      * @param {Number} height The target element height
8299      */
8300     realign : function(l, t, w, h){
8301         if(!this.el){
8302             return;
8303         }
8304         var a = this.adjusts, d = this.el.dom, s = d.style;
8305         var iea = 0;
8306         s.left = (l+a.l)+"px";
8307         s.top = (t+a.t)+"px";
8308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8309  
8310         if(s.width != sws || s.height != shs){
8311             s.width = sws;
8312             s.height = shs;
8313             if(!Roo.isIE){
8314                 var cn = d.childNodes;
8315                 var sww = Math.max(0, (sw-12))+"px";
8316                 cn[0].childNodes[1].style.width = sww;
8317                 cn[1].childNodes[1].style.width = sww;
8318                 cn[2].childNodes[1].style.width = sww;
8319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8320             }
8321         }
8322     },
8323
8324     /**
8325      * Hides this shadow
8326      */
8327     hide : function(){
8328         if(this.el){
8329             this.el.dom.style.display = "none";
8330             Roo.Shadow.Pool.push(this.el);
8331             delete this.el;
8332         }
8333     },
8334
8335     /**
8336      * Adjust the z-index of this shadow
8337      * @param {Number} zindex The new z-index
8338      */
8339     setZIndex : function(z){
8340         this.zIndex = z;
8341         if(this.el){
8342             this.el.setStyle("z-index", z);
8343         }
8344     }
8345 };
8346
8347 // Private utility class that manages the internal Shadow cache
8348 Roo.Shadow.Pool = function(){
8349     var p = [];
8350     var markup = Roo.isIE ?
8351                  '<div class="x-ie-shadow"></div>' :
8352                  '<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>';
8353     return {
8354         pull : function(){
8355             var sh = p.shift();
8356             if(!sh){
8357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8358                 sh.autoBoxAdjust = false;
8359             }
8360             return sh;
8361         },
8362
8363         push : function(sh){
8364             p.push(sh);
8365         }
8366     };
8367 }();/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377
8378 /**
8379  * @class Roo.BoxComponent
8380  * @extends Roo.Component
8381  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8382  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8383  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8384  * layout containers.
8385  * @constructor
8386  * @param {Roo.Element/String/Object} config The configuration options.
8387  */
8388 Roo.BoxComponent = function(config){
8389     Roo.Component.call(this, config);
8390     this.addEvents({
8391         /**
8392          * @event resize
8393          * Fires after the component is resized.
8394              * @param {Roo.Component} this
8395              * @param {Number} adjWidth The box-adjusted width that was set
8396              * @param {Number} adjHeight The box-adjusted height that was set
8397              * @param {Number} rawWidth The width that was originally specified
8398              * @param {Number} rawHeight The height that was originally specified
8399              */
8400         resize : true,
8401         /**
8402          * @event move
8403          * Fires after the component is moved.
8404              * @param {Roo.Component} this
8405              * @param {Number} x The new x position
8406              * @param {Number} y The new y position
8407              */
8408         move : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.BoxComponent, Roo.Component, {
8413     // private, set in afterRender to signify that the component has been rendered
8414     boxReady : false,
8415     // private, used to defer height settings to subclasses
8416     deferHeight: false,
8417     /** @cfg {Number} width
8418      * width (optional) size of component
8419      */
8420      /** @cfg {Number} height
8421      * height (optional) size of component
8422      */
8423      
8424     /**
8425      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8426      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8427      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8428      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8429      * @return {Roo.BoxComponent} this
8430      */
8431     setSize : function(w, h){
8432         // support for standard size objects
8433         if(typeof w == 'object'){
8434             h = w.height;
8435             w = w.width;
8436         }
8437         // not rendered
8438         if(!this.boxReady){
8439             this.width = w;
8440             this.height = h;
8441             return this;
8442         }
8443
8444         // prevent recalcs when not needed
8445         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8446             return this;
8447         }
8448         this.lastSize = {width: w, height: h};
8449
8450         var adj = this.adjustSize(w, h);
8451         var aw = adj.width, ah = adj.height;
8452         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8453             var rz = this.getResizeEl();
8454             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8455                 rz.setSize(aw, ah);
8456             }else if(!this.deferHeight && ah !== undefined){
8457                 rz.setHeight(ah);
8458             }else if(aw !== undefined){
8459                 rz.setWidth(aw);
8460             }
8461             this.onResize(aw, ah, w, h);
8462             this.fireEvent('resize', this, aw, ah, w, h);
8463         }
8464         return this;
8465     },
8466
8467     /**
8468      * Gets the current size of the component's underlying element.
8469      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8470      */
8471     getSize : function(){
8472         return this.el.getSize();
8473     },
8474
8475     /**
8476      * Gets the current XY position of the component's underlying element.
8477      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8478      * @return {Array} The XY position of the element (e.g., [100, 200])
8479      */
8480     getPosition : function(local){
8481         if(local === true){
8482             return [this.el.getLeft(true), this.el.getTop(true)];
8483         }
8484         return this.xy || this.el.getXY();
8485     },
8486
8487     /**
8488      * Gets the current box measurements of the component's underlying element.
8489      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8490      * @returns {Object} box An object in the format {x, y, width, height}
8491      */
8492     getBox : function(local){
8493         var s = this.el.getSize();
8494         if(local){
8495             s.x = this.el.getLeft(true);
8496             s.y = this.el.getTop(true);
8497         }else{
8498             var xy = this.xy || this.el.getXY();
8499             s.x = xy[0];
8500             s.y = xy[1];
8501         }
8502         return s;
8503     },
8504
8505     /**
8506      * Sets the current box measurements of the component's underlying element.
8507      * @param {Object} box An object in the format {x, y, width, height}
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     updateBox : function(box){
8511         this.setSize(box.width, box.height);
8512         this.setPagePosition(box.x, box.y);
8513         return this;
8514     },
8515
8516     // protected
8517     getResizeEl : function(){
8518         return this.resizeEl || this.el;
8519     },
8520
8521     // protected
8522     getPositionEl : function(){
8523         return this.positionEl || this.el;
8524     },
8525
8526     /**
8527      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8528      * This method fires the move event.
8529      * @param {Number} left The new left
8530      * @param {Number} top The new top
8531      * @returns {Roo.BoxComponent} this
8532      */
8533     setPosition : function(x, y){
8534         this.x = x;
8535         this.y = y;
8536         if(!this.boxReady){
8537             return this;
8538         }
8539         var adj = this.adjustPosition(x, y);
8540         var ax = adj.x, ay = adj.y;
8541
8542         var el = this.getPositionEl();
8543         if(ax !== undefined || ay !== undefined){
8544             if(ax !== undefined && ay !== undefined){
8545                 el.setLeftTop(ax, ay);
8546             }else if(ax !== undefined){
8547                 el.setLeft(ax);
8548             }else if(ay !== undefined){
8549                 el.setTop(ay);
8550             }
8551             this.onPosition(ax, ay);
8552             this.fireEvent('move', this, ax, ay);
8553         }
8554         return this;
8555     },
8556
8557     /**
8558      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8559      * This method fires the move event.
8560      * @param {Number} x The new x position
8561      * @param {Number} y The new y position
8562      * @returns {Roo.BoxComponent} this
8563      */
8564     setPagePosition : function(x, y){
8565         this.pageX = x;
8566         this.pageY = y;
8567         if(!this.boxReady){
8568             return;
8569         }
8570         if(x === undefined || y === undefined){ // cannot translate undefined points
8571             return;
8572         }
8573         var p = this.el.translatePoints(x, y);
8574         this.setPosition(p.left, p.top);
8575         return this;
8576     },
8577
8578     // private
8579     onRender : function(ct, position){
8580         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8581         if(this.resizeEl){
8582             this.resizeEl = Roo.get(this.resizeEl);
8583         }
8584         if(this.positionEl){
8585             this.positionEl = Roo.get(this.positionEl);
8586         }
8587     },
8588
8589     // private
8590     afterRender : function(){
8591         Roo.BoxComponent.superclass.afterRender.call(this);
8592         this.boxReady = true;
8593         this.setSize(this.width, this.height);
8594         if(this.x || this.y){
8595             this.setPosition(this.x, this.y);
8596         }
8597         if(this.pageX || this.pageY){
8598             this.setPagePosition(this.pageX, this.pageY);
8599         }
8600     },
8601
8602     /**
8603      * Force the component's size to recalculate based on the underlying element's current height and width.
8604      * @returns {Roo.BoxComponent} this
8605      */
8606     syncSize : function(){
8607         delete this.lastSize;
8608         this.setSize(this.el.getWidth(), this.el.getHeight());
8609         return this;
8610     },
8611
8612     /**
8613      * Called after the component is resized, this method is empty by default but can be implemented by any
8614      * subclass that needs to perform custom logic after a resize occurs.
8615      * @param {Number} adjWidth The box-adjusted width that was set
8616      * @param {Number} adjHeight The box-adjusted height that was set
8617      * @param {Number} rawWidth The width that was originally specified
8618      * @param {Number} rawHeight The height that was originally specified
8619      */
8620     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8621
8622     },
8623
8624     /**
8625      * Called after the component is moved, this method is empty by default but can be implemented by any
8626      * subclass that needs to perform custom logic after a move occurs.
8627      * @param {Number} x The new x position
8628      * @param {Number} y The new y position
8629      */
8630     onPosition : function(x, y){
8631
8632     },
8633
8634     // private
8635     adjustSize : function(w, h){
8636         if(this.autoWidth){
8637             w = 'auto';
8638         }
8639         if(this.autoHeight){
8640             h = 'auto';
8641         }
8642         return {width : w, height: h};
8643     },
8644
8645     // private
8646     adjustPosition : function(x, y){
8647         return {x : x, y: y};
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660
8661 /**
8662  * @class Roo.SplitBar
8663  * @extends Roo.util.Observable
8664  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8665  * <br><br>
8666  * Usage:
8667  * <pre><code>
8668 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8669                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8670 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8671 split.minSize = 100;
8672 split.maxSize = 600;
8673 split.animate = true;
8674 split.on('moved', splitterMoved);
8675 </code></pre>
8676  * @constructor
8677  * Create a new SplitBar
8678  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8679  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8680  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8681  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8682                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8683                         position of the SplitBar).
8684  */
8685 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8686     
8687     /** @private */
8688     this.el = Roo.get(dragElement, true);
8689     this.el.dom.unselectable = "on";
8690     /** @private */
8691     this.resizingEl = Roo.get(resizingElement, true);
8692
8693     /**
8694      * @private
8695      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8696      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8697      * @type Number
8698      */
8699     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8700     
8701     /**
8702      * The minimum size of the resizing element. (Defaults to 0)
8703      * @type Number
8704      */
8705     this.minSize = 0;
8706     
8707     /**
8708      * The maximum size of the resizing element. (Defaults to 2000)
8709      * @type Number
8710      */
8711     this.maxSize = 2000;
8712     
8713     /**
8714      * Whether to animate the transition to the new size
8715      * @type Boolean
8716      */
8717     this.animate = false;
8718     
8719     /**
8720      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8721      * @type Boolean
8722      */
8723     this.useShim = false;
8724     
8725     /** @private */
8726     this.shim = null;
8727     
8728     if(!existingProxy){
8729         /** @private */
8730         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8731     }else{
8732         this.proxy = Roo.get(existingProxy).dom;
8733     }
8734     /** @private */
8735     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8736     
8737     /** @private */
8738     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8739     
8740     /** @private */
8741     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8742     
8743     /** @private */
8744     this.dragSpecs = {};
8745     
8746     /**
8747      * @private The adapter to use to positon and resize elements
8748      */
8749     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8750     this.adapter.init(this);
8751     
8752     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8753         /** @private */
8754         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8755         this.el.addClass("x-splitbar-h");
8756     }else{
8757         /** @private */
8758         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8759         this.el.addClass("x-splitbar-v");
8760     }
8761     
8762     this.addEvents({
8763         /**
8764          * @event resize
8765          * Fires when the splitter is moved (alias for {@link #event-moved})
8766          * @param {Roo.SplitBar} this
8767          * @param {Number} newSize the new width or height
8768          */
8769         "resize" : true,
8770         /**
8771          * @event moved
8772          * Fires when the splitter is moved
8773          * @param {Roo.SplitBar} this
8774          * @param {Number} newSize the new width or height
8775          */
8776         "moved" : true,
8777         /**
8778          * @event beforeresize
8779          * Fires before the splitter is dragged
8780          * @param {Roo.SplitBar} this
8781          */
8782         "beforeresize" : true,
8783
8784         "beforeapply" : true
8785     });
8786
8787     Roo.util.Observable.call(this);
8788 };
8789
8790 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8791     onStartProxyDrag : function(x, y){
8792         this.fireEvent("beforeresize", this);
8793         if(!this.overlay){
8794             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8795             o.unselectable();
8796             o.enableDisplayMode("block");
8797             // all splitbars share the same overlay
8798             Roo.SplitBar.prototype.overlay = o;
8799         }
8800         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8801         this.overlay.show();
8802         Roo.get(this.proxy).setDisplayed("block");
8803         var size = this.adapter.getElementSize(this);
8804         this.activeMinSize = this.getMinimumSize();;
8805         this.activeMaxSize = this.getMaximumSize();;
8806         var c1 = size - this.activeMinSize;
8807         var c2 = Math.max(this.activeMaxSize - size, 0);
8808         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8809             this.dd.resetConstraints();
8810             this.dd.setXConstraint(
8811                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8812                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8813             );
8814             this.dd.setYConstraint(0, 0);
8815         }else{
8816             this.dd.resetConstraints();
8817             this.dd.setXConstraint(0, 0);
8818             this.dd.setYConstraint(
8819                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8821             );
8822          }
8823         this.dragSpecs.startSize = size;
8824         this.dragSpecs.startPoint = [x, y];
8825         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8826     },
8827     
8828     /** 
8829      * @private Called after the drag operation by the DDProxy
8830      */
8831     onEndProxyDrag : function(e){
8832         Roo.get(this.proxy).setDisplayed(false);
8833         var endPoint = Roo.lib.Event.getXY(e);
8834         if(this.overlay){
8835             this.overlay.hide();
8836         }
8837         var newSize;
8838         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8839             newSize = this.dragSpecs.startSize + 
8840                 (this.placement == Roo.SplitBar.LEFT ?
8841                     endPoint[0] - this.dragSpecs.startPoint[0] :
8842                     this.dragSpecs.startPoint[0] - endPoint[0]
8843                 );
8844         }else{
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.TOP ?
8847                     endPoint[1] - this.dragSpecs.startPoint[1] :
8848                     this.dragSpecs.startPoint[1] - endPoint[1]
8849                 );
8850         }
8851         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8852         if(newSize != this.dragSpecs.startSize){
8853             if(this.fireEvent('beforeapply', this, newSize) !== false){
8854                 this.adapter.setElementSize(this, newSize);
8855                 this.fireEvent("moved", this, newSize);
8856                 this.fireEvent("resize", this, newSize);
8857             }
8858         }
8859     },
8860     
8861     /**
8862      * Get the adapter this SplitBar uses
8863      * @return The adapter object
8864      */
8865     getAdapter : function(){
8866         return this.adapter;
8867     },
8868     
8869     /**
8870      * Set the adapter this SplitBar uses
8871      * @param {Object} adapter A SplitBar adapter object
8872      */
8873     setAdapter : function(adapter){
8874         this.adapter = adapter;
8875         this.adapter.init(this);
8876     },
8877     
8878     /**
8879      * Gets the minimum size for the resizing element
8880      * @return {Number} The minimum size
8881      */
8882     getMinimumSize : function(){
8883         return this.minSize;
8884     },
8885     
8886     /**
8887      * Sets the minimum size for the resizing element
8888      * @param {Number} minSize The minimum size
8889      */
8890     setMinimumSize : function(minSize){
8891         this.minSize = minSize;
8892     },
8893     
8894     /**
8895      * Gets the maximum size for the resizing element
8896      * @return {Number} The maximum size
8897      */
8898     getMaximumSize : function(){
8899         return this.maxSize;
8900     },
8901     
8902     /**
8903      * Sets the maximum size for the resizing element
8904      * @param {Number} maxSize The maximum size
8905      */
8906     setMaximumSize : function(maxSize){
8907         this.maxSize = maxSize;
8908     },
8909     
8910     /**
8911      * Sets the initialize size for the resizing element
8912      * @param {Number} size The initial size
8913      */
8914     setCurrentSize : function(size){
8915         var oldAnimate = this.animate;
8916         this.animate = false;
8917         this.adapter.setElementSize(this, size);
8918         this.animate = oldAnimate;
8919     },
8920     
8921     /**
8922      * Destroy this splitbar. 
8923      * @param {Boolean} removeEl True to remove the element
8924      */
8925     destroy : function(removeEl){
8926         if(this.shim){
8927             this.shim.remove();
8928         }
8929         this.dd.unreg();
8930         this.proxy.parentNode.removeChild(this.proxy);
8931         if(removeEl){
8932             this.el.remove();
8933         }
8934     }
8935 });
8936
8937 /**
8938  * @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.
8939  */
8940 Roo.SplitBar.createProxy = function(dir){
8941     var proxy = new Roo.Element(document.createElement("div"));
8942     proxy.unselectable();
8943     var cls = 'x-splitbar-proxy';
8944     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8945     document.body.appendChild(proxy.dom);
8946     return proxy.dom;
8947 };
8948
8949 /** 
8950  * @class Roo.SplitBar.BasicLayoutAdapter
8951  * Default Adapter. It assumes the splitter and resizing element are not positioned
8952  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8953  */
8954 Roo.SplitBar.BasicLayoutAdapter = function(){
8955 };
8956
8957 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8958     // do nothing for now
8959     init : function(s){
8960     
8961     },
8962     /**
8963      * Called before drag operations to get the current size of the resizing element. 
8964      * @param {Roo.SplitBar} s The SplitBar using this adapter
8965      */
8966      getElementSize : function(s){
8967         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8968             return s.resizingEl.getWidth();
8969         }else{
8970             return s.resizingEl.getHeight();
8971         }
8972     },
8973     
8974     /**
8975      * Called after drag operations to set the size of the resizing element.
8976      * @param {Roo.SplitBar} s The SplitBar using this adapter
8977      * @param {Number} newSize The new size to set
8978      * @param {Function} onComplete A function to be invoked when resizing is complete
8979      */
8980     setElementSize : function(s, newSize, onComplete){
8981         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8982             if(!s.animate){
8983                 s.resizingEl.setWidth(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }else{
8991             
8992             if(!s.animate){
8993                 s.resizingEl.setHeight(newSize);
8994                 if(onComplete){
8995                     onComplete(s, newSize);
8996                 }
8997             }else{
8998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8999             }
9000         }
9001     }
9002 };
9003
9004 /** 
9005  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9006  * @extends Roo.SplitBar.BasicLayoutAdapter
9007  * Adapter that  moves the splitter element to align with the resized sizing element. 
9008  * Used with an absolute positioned SplitBar.
9009  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9010  * document.body, make sure you assign an id to the body element.
9011  */
9012 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9013     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9014     this.container = Roo.get(container);
9015 };
9016
9017 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9018     init : function(s){
9019         this.basic.init(s);
9020     },
9021     
9022     getElementSize : function(s){
9023         return this.basic.getElementSize(s);
9024     },
9025     
9026     setElementSize : function(s, newSize, onComplete){
9027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9028     },
9029     
9030     moveSplitter : function(s){
9031         var yes = Roo.SplitBar;
9032         switch(s.placement){
9033             case yes.LEFT:
9034                 s.el.setX(s.resizingEl.getRight());
9035                 break;
9036             case yes.RIGHT:
9037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9038                 break;
9039             case yes.TOP:
9040                 s.el.setY(s.resizingEl.getBottom());
9041                 break;
9042             case yes.BOTTOM:
9043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9044                 break;
9045         }
9046     }
9047 };
9048
9049 /**
9050  * Orientation constant - Create a vertical SplitBar
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.VERTICAL = 1;
9055
9056 /**
9057  * Orientation constant - Create a horizontal SplitBar
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.HORIZONTAL = 2;
9062
9063 /**
9064  * Placement constant - The resizing element is to the left of the splitter element
9065  * @static
9066  * @type Number
9067  */
9068 Roo.SplitBar.LEFT = 1;
9069
9070 /**
9071  * Placement constant - The resizing element is to the right of the splitter element
9072  * @static
9073  * @type Number
9074  */
9075 Roo.SplitBar.RIGHT = 2;
9076
9077 /**
9078  * Placement constant - The resizing element is positioned above the splitter element
9079  * @static
9080  * @type Number
9081  */
9082 Roo.SplitBar.TOP = 3;
9083
9084 /**
9085  * Placement constant - The resizing element is positioned under splitter element
9086  * @static
9087  * @type Number
9088  */
9089 Roo.SplitBar.BOTTOM = 4;
9090 /*
9091  * Based on:
9092  * Ext JS Library 1.1.1
9093  * Copyright(c) 2006-2007, Ext JS, LLC.
9094  *
9095  * Originally Released Under LGPL - original licence link has changed is not relivant.
9096  *
9097  * Fork - LGPL
9098  * <script type="text/javascript">
9099  */
9100
9101 /**
9102  * @class Roo.View
9103  * @extends Roo.util.Observable
9104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9105  * This class also supports single and multi selection modes. <br>
9106  * Create a data model bound view:
9107  <pre><code>
9108  var store = new Roo.data.Store(...);
9109
9110  var view = new Roo.View({
9111     el : "my-element",
9112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9113  
9114     singleSelect: true,
9115     selectedClass: "ydataview-selected",
9116     store: store
9117  });
9118
9119  // listen for node click?
9120  view.on("click", function(vw, index, node, e){
9121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9122  });
9123
9124  // load XML data
9125  dataModel.load("foobar.xml");
9126  </code></pre>
9127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9128  * <br><br>
9129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9131  * 
9132  * Note: old style constructor is still suported (container, template, config)
9133  * 
9134  * @constructor
9135  * Create a new View
9136  * @param {Object} config The config object
9137  * 
9138  */
9139 Roo.View = function(config, depreciated_tpl, depreciated_config){
9140     
9141     if (typeof(depreciated_tpl) == 'undefined') {
9142         // new way.. - universal constructor.
9143         Roo.apply(this, config);
9144         this.el  = Roo.get(this.el);
9145     } else {
9146         // old format..
9147         this.el  = Roo.get(config);
9148         this.tpl = depreciated_tpl;
9149         Roo.apply(this, depreciated_config);
9150     }
9151      
9152     
9153     if(typeof(this.tpl) == "string"){
9154         this.tpl = new Roo.Template(this.tpl);
9155     } else {
9156         // support xtype ctors..
9157         this.tpl = new Roo.factory(this.tpl, Roo);
9158     }
9159     
9160     
9161     this.tpl.compile();
9162    
9163
9164      
9165     /** @private */
9166     this.addEvents({
9167         /**
9168          * @event beforeclick
9169          * Fires before a click is processed. Returns false to cancel the default action.
9170          * @param {Roo.View} this
9171          * @param {Number} index The index of the target node
9172          * @param {HTMLElement} node The target node
9173          * @param {Roo.EventObject} e The raw event object
9174          */
9175             "beforeclick" : true,
9176         /**
9177          * @event click
9178          * Fires when a template node is clicked.
9179          * @param {Roo.View} this
9180          * @param {Number} index The index of the target node
9181          * @param {HTMLElement} node The target node
9182          * @param {Roo.EventObject} e The raw event object
9183          */
9184             "click" : true,
9185         /**
9186          * @event dblclick
9187          * Fires when a template node is double clicked.
9188          * @param {Roo.View} this
9189          * @param {Number} index The index of the target node
9190          * @param {HTMLElement} node The target node
9191          * @param {Roo.EventObject} e The raw event object
9192          */
9193             "dblclick" : true,
9194         /**
9195          * @event contextmenu
9196          * Fires when a template node is right clicked.
9197          * @param {Roo.View} this
9198          * @param {Number} index The index of the target node
9199          * @param {HTMLElement} node The target node
9200          * @param {Roo.EventObject} e The raw event object
9201          */
9202             "contextmenu" : true,
9203         /**
9204          * @event selectionchange
9205          * Fires when the selected nodes change.
9206          * @param {Roo.View} this
9207          * @param {Array} selections Array of the selected nodes
9208          */
9209             "selectionchange" : true,
9210     
9211         /**
9212          * @event beforeselect
9213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9214          * @param {Roo.View} this
9215          * @param {HTMLElement} node The node to be selected
9216          * @param {Array} selections Array of currently selected nodes
9217          */
9218             "beforeselect" : true,
9219         /**
9220          * @event preparedata
9221          * Fires on every row to render, to allow you to change the data.
9222          * @param {Roo.View} this
9223          * @param {Object} data to be rendered (change this)
9224          */
9225           "preparedata" : true
9226         });
9227
9228     this.el.on({
9229         "click": this.onClick,
9230         "dblclick": this.onDblClick,
9231         "contextmenu": this.onContextMenu,
9232         scope:this
9233     });
9234
9235     this.selections = [];
9236     this.nodes = [];
9237     this.cmp = new Roo.CompositeElementLite([]);
9238     if(this.store){
9239         this.store = Roo.factory(this.store, Roo.data);
9240         this.setStore(this.store, true);
9241     }
9242     Roo.View.superclass.constructor.call(this);
9243 };
9244
9245 Roo.extend(Roo.View, Roo.util.Observable, {
9246     
9247      /**
9248      * @cfg {Roo.data.Store} store Data store to load data from.
9249      */
9250     store : false,
9251     
9252     /**
9253      * @cfg {String|Roo.Element} el The container element.
9254      */
9255     el : '',
9256     
9257     /**
9258      * @cfg {String|Roo.Template} tpl The template used by this View 
9259      */
9260     tpl : false,
9261     
9262     /**
9263      * @cfg {String} selectedClass The css class to add to selected nodes
9264      */
9265     selectedClass : "x-view-selected",
9266      /**
9267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9268      */
9269     emptyText : "",
9270     /**
9271      * @cfg {Boolean} multiSelect Allow multiple selection
9272      */
9273     multiSelect : false,
9274     /**
9275      * @cfg {Boolean} singleSelect Allow single selection
9276      */
9277     singleSelect:  false,
9278     
9279     /**
9280      * @cfg {Boolean} toggleSelect - selecting 
9281      */
9282     toggleSelect : false,
9283     
9284     /**
9285      * Returns the element this view is bound to.
9286      * @return {Roo.Element}
9287      */
9288     getEl : function(){
9289         return this.el;
9290     },
9291
9292     /**
9293      * Refreshes the view.
9294      */
9295     refresh : function(){
9296         var t = this.tpl;
9297         this.clearSelections();
9298         this.el.update("");
9299         var html = [];
9300         var records = this.store.getRange();
9301         if(records.length < 1){
9302             this.el.update(this.emptyText);
9303             return;
9304         }
9305         for(var i = 0, len = records.length; i < len; i++){
9306             var data = this.prepareData(records[i].data, i, records[i]);
9307             this.fireEvent("preparedata", this, data, i, records[i]);
9308             html[html.length] = t.apply(data);
9309         }
9310         this.el.update(html.join(""));
9311         this.nodes = this.el.dom.childNodes;
9312         this.updateIndexes(0);
9313     },
9314
9315     /**
9316      * Function to override to reformat the data that is sent to
9317      * the template for each node.
9318      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9319      * a JSON object for an UpdateManager bound view).
9320      */
9321     prepareData : function(data){
9322         return data;
9323     },
9324
9325     onUpdate : function(ds, record){
9326         this.clearSelections();
9327         var index = this.store.indexOf(record);
9328         var n = this.nodes[index];
9329         this.tpl.insertBefore(n, this.prepareData(record.data));
9330         n.parentNode.removeChild(n);
9331         this.updateIndexes(index, index);
9332     },
9333
9334     onAdd : function(ds, records, index){
9335         this.clearSelections();
9336         if(this.nodes.length == 0){
9337             this.refresh();
9338             return;
9339         }
9340         var n = this.nodes[index];
9341         for(var i = 0, len = records.length; i < len; i++){
9342             var d = this.prepareData(records[i].data);
9343             if(n){
9344                 this.tpl.insertBefore(n, d);
9345             }else{
9346                 this.tpl.append(this.el, d);
9347             }
9348         }
9349         this.updateIndexes(index);
9350     },
9351
9352     onRemove : function(ds, record, index){
9353         this.clearSelections();
9354         this.el.dom.removeChild(this.nodes[index]);
9355         this.updateIndexes(index);
9356     },
9357
9358     /**
9359      * Refresh an individual node.
9360      * @param {Number} index
9361      */
9362     refreshNode : function(index){
9363         this.onUpdate(this.store, this.store.getAt(index));
9364     },
9365
9366     updateIndexes : function(startIndex, endIndex){
9367         var ns = this.nodes;
9368         startIndex = startIndex || 0;
9369         endIndex = endIndex || ns.length - 1;
9370         for(var i = startIndex; i <= endIndex; i++){
9371             ns[i].nodeIndex = i;
9372         }
9373     },
9374
9375     /**
9376      * Changes the data store this view uses and refresh the view.
9377      * @param {Store} store
9378      */
9379     setStore : function(store, initial){
9380         if(!initial && this.store){
9381             this.store.un("datachanged", this.refresh);
9382             this.store.un("add", this.onAdd);
9383             this.store.un("remove", this.onRemove);
9384             this.store.un("update", this.onUpdate);
9385             this.store.un("clear", this.refresh);
9386         }
9387         if(store){
9388           
9389             store.on("datachanged", this.refresh, this);
9390             store.on("add", this.onAdd, this);
9391             store.on("remove", this.onRemove, this);
9392             store.on("update", this.onUpdate, this);
9393             store.on("clear", this.refresh, this);
9394         }
9395         
9396         if(store){
9397             this.refresh();
9398         }
9399     },
9400
9401     /**
9402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9403      * @param {HTMLElement} node
9404      * @return {HTMLElement} The template node
9405      */
9406     findItemFromChild : function(node){
9407         var el = this.el.dom;
9408         if(!node || node.parentNode == el){
9409                     return node;
9410             }
9411             var p = node.parentNode;
9412             while(p && p != el){
9413             if(p.parentNode == el){
9414                 return p;
9415             }
9416             p = p.parentNode;
9417         }
9418             return null;
9419     },
9420
9421     /** @ignore */
9422     onClick : function(e){
9423         var item = this.findItemFromChild(e.getTarget());
9424         if(item){
9425             var index = this.indexOf(item);
9426             if(this.onItemClick(item, index, e) !== false){
9427                 this.fireEvent("click", this, index, item, e);
9428             }
9429         }else{
9430             this.clearSelections();
9431         }
9432     },
9433
9434     /** @ignore */
9435     onContextMenu : function(e){
9436         var item = this.findItemFromChild(e.getTarget());
9437         if(item){
9438             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9439         }
9440     },
9441
9442     /** @ignore */
9443     onDblClick : function(e){
9444         var item = this.findItemFromChild(e.getTarget());
9445         if(item){
9446             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9447         }
9448     },
9449
9450     onItemClick : function(item, index, e)
9451     {
9452         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9453             return false;
9454         }
9455         if (this.toggleSelect) {
9456             var m = this.isSelected(item) ? 'unselect' : 'select';
9457             Roo.log(m);
9458             var _t = this;
9459             _t[m](item, true, false);
9460             return true;
9461         }
9462         if(this.multiSelect || this.singleSelect){
9463             if(this.multiSelect && e.shiftKey && this.lastSelection){
9464                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9465             }else{
9466                 this.select(item, this.multiSelect && e.ctrlKey);
9467                 this.lastSelection = item;
9468             }
9469             e.preventDefault();
9470         }
9471         return true;
9472     },
9473
9474     /**
9475      * Get the number of selected nodes.
9476      * @return {Number}
9477      */
9478     getSelectionCount : function(){
9479         return this.selections.length;
9480     },
9481
9482     /**
9483      * Get the currently selected nodes.
9484      * @return {Array} An array of HTMLElements
9485      */
9486     getSelectedNodes : function(){
9487         return this.selections;
9488     },
9489
9490     /**
9491      * Get the indexes of the selected nodes.
9492      * @return {Array}
9493      */
9494     getSelectedIndexes : function(){
9495         var indexes = [], s = this.selections;
9496         for(var i = 0, len = s.length; i < len; i++){
9497             indexes.push(s[i].nodeIndex);
9498         }
9499         return indexes;
9500     },
9501
9502     /**
9503      * Clear all selections
9504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9505      */
9506     clearSelections : function(suppressEvent){
9507         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9508             this.cmp.elements = this.selections;
9509             this.cmp.removeClass(this.selectedClass);
9510             this.selections = [];
9511             if(!suppressEvent){
9512                 this.fireEvent("selectionchange", this, this.selections);
9513             }
9514         }
9515     },
9516
9517     /**
9518      * Returns true if the passed node is selected
9519      * @param {HTMLElement/Number} node The node or node index
9520      * @return {Boolean}
9521      */
9522     isSelected : function(node){
9523         var s = this.selections;
9524         if(s.length < 1){
9525             return false;
9526         }
9527         node = this.getNode(node);
9528         return s.indexOf(node) !== -1;
9529     },
9530
9531     /**
9532      * Selects nodes.
9533      * @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
9534      * @param {Boolean} keepExisting (optional) true to keep existing selections
9535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9536      */
9537     select : function(nodeInfo, keepExisting, suppressEvent){
9538         if(nodeInfo instanceof Array){
9539             if(!keepExisting){
9540                 this.clearSelections(true);
9541             }
9542             for(var i = 0, len = nodeInfo.length; i < len; i++){
9543                 this.select(nodeInfo[i], true, true);
9544             }
9545             return;
9546         } 
9547         var node = this.getNode(nodeInfo);
9548         if(!node || this.isSelected(node)){
9549             return; // already selected.
9550         }
9551         if(!keepExisting){
9552             this.clearSelections(true);
9553         }
9554         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9555             Roo.fly(node).addClass(this.selectedClass);
9556             this.selections.push(node);
9557             if(!suppressEvent){
9558                 this.fireEvent("selectionchange", this, this.selections);
9559             }
9560         }
9561         
9562         
9563     },
9564       /**
9565      * Unselects nodes.
9566      * @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
9567      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9569      */
9570     unselect : function(nodeInfo, keepExisting, suppressEvent)
9571     {
9572         if(nodeInfo instanceof Array){
9573             Roo.each(this.selections, function(s) {
9574                 this.unselect(s, nodeInfo);
9575             }, this);
9576             return;
9577         }
9578         var node = this.getNode(nodeInfo);
9579         if(!node || !this.isSelected(node)){
9580             Roo.log("not selected");
9581             return; // not selected.
9582         }
9583         // fireevent???
9584         var ns = [];
9585         Roo.each(this.selections, function(s) {
9586             if (s == node ) {
9587                 Roo.fly(node).removeClass(this.selectedClass);
9588
9589                 return;
9590             }
9591             ns.push(s);
9592         },this);
9593         
9594         this.selections= ns;
9595         this.fireEvent("selectionchange", this, this.selections);
9596     },
9597
9598     /**
9599      * Gets a template node.
9600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9601      * @return {HTMLElement} The node or null if it wasn't found
9602      */
9603     getNode : function(nodeInfo){
9604         if(typeof nodeInfo == "string"){
9605             return document.getElementById(nodeInfo);
9606         }else if(typeof nodeInfo == "number"){
9607             return this.nodes[nodeInfo];
9608         }
9609         return nodeInfo;
9610     },
9611
9612     /**
9613      * Gets a range template nodes.
9614      * @param {Number} startIndex
9615      * @param {Number} endIndex
9616      * @return {Array} An array of nodes
9617      */
9618     getNodes : function(start, end){
9619         var ns = this.nodes;
9620         start = start || 0;
9621         end = typeof end == "undefined" ? ns.length - 1 : end;
9622         var nodes = [];
9623         if(start <= end){
9624             for(var i = start; i <= end; i++){
9625                 nodes.push(ns[i]);
9626             }
9627         } else{
9628             for(var i = start; i >= end; i--){
9629                 nodes.push(ns[i]);
9630             }
9631         }
9632         return nodes;
9633     },
9634
9635     /**
9636      * Finds the index of the passed node
9637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9638      * @return {Number} The index of the node or -1
9639      */
9640     indexOf : function(node){
9641         node = this.getNode(node);
9642         if(typeof node.nodeIndex == "number"){
9643             return node.nodeIndex;
9644         }
9645         var ns = this.nodes;
9646         for(var i = 0, len = ns.length; i < len; i++){
9647             if(ns[i] == node){
9648                 return i;
9649             }
9650         }
9651         return -1;
9652     }
9653 });
9654 /*
9655  * Based on:
9656  * Ext JS Library 1.1.1
9657  * Copyright(c) 2006-2007, Ext JS, LLC.
9658  *
9659  * Originally Released Under LGPL - original licence link has changed is not relivant.
9660  *
9661  * Fork - LGPL
9662  * <script type="text/javascript">
9663  */
9664
9665 /**
9666  * @class Roo.JsonView
9667  * @extends Roo.View
9668  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9669 <pre><code>
9670 var view = new Roo.JsonView({
9671     container: "my-element",
9672     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9673     multiSelect: true, 
9674     jsonRoot: "data" 
9675 });
9676
9677 // listen for node click?
9678 view.on("click", function(vw, index, node, e){
9679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9680 });
9681
9682 // direct load of JSON data
9683 view.load("foobar.php");
9684
9685 // Example from my blog list
9686 var tpl = new Roo.Template(
9687     '&lt;div class="entry"&gt;' +
9688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9690     "&lt;/div&gt;&lt;hr /&gt;"
9691 );
9692
9693 var moreView = new Roo.JsonView({
9694     container :  "entry-list", 
9695     template : tpl,
9696     jsonRoot: "posts"
9697 });
9698 moreView.on("beforerender", this.sortEntries, this);
9699 moreView.load({
9700     url: "/blog/get-posts.php",
9701     params: "allposts=true",
9702     text: "Loading Blog Entries..."
9703 });
9704 </code></pre>
9705
9706 * Note: old code is supported with arguments : (container, template, config)
9707
9708
9709  * @constructor
9710  * Create a new JsonView
9711  * 
9712  * @param {Object} config The config object
9713  * 
9714  */
9715 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9716     
9717     
9718     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9719
9720     var um = this.el.getUpdateManager();
9721     um.setRenderer(this);
9722     um.on("update", this.onLoad, this);
9723     um.on("failure", this.onLoadException, this);
9724
9725     /**
9726      * @event beforerender
9727      * Fires before rendering of the downloaded JSON data.
9728      * @param {Roo.JsonView} this
9729      * @param {Object} data The JSON data loaded
9730      */
9731     /**
9732      * @event load
9733      * Fires when data is loaded.
9734      * @param {Roo.JsonView} this
9735      * @param {Object} data The JSON data loaded
9736      * @param {Object} response The raw Connect response object
9737      */
9738     /**
9739      * @event loadexception
9740      * Fires when loading fails.
9741      * @param {Roo.JsonView} this
9742      * @param {Object} response The raw Connect response object
9743      */
9744     this.addEvents({
9745         'beforerender' : true,
9746         'load' : true,
9747         'loadexception' : true
9748     });
9749 };
9750 Roo.extend(Roo.JsonView, Roo.View, {
9751     /**
9752      * @type {String} The root property in the loaded JSON object that contains the data
9753      */
9754     jsonRoot : "",
9755
9756     /**
9757      * Refreshes the view.
9758      */
9759     refresh : function(){
9760         this.clearSelections();
9761         this.el.update("");
9762         var html = [];
9763         var o = this.jsonData;
9764         if(o && o.length > 0){
9765             for(var i = 0, len = o.length; i < len; i++){
9766                 var data = this.prepareData(o[i], i, o);
9767                 html[html.length] = this.tpl.apply(data);
9768             }
9769         }else{
9770             html.push(this.emptyText);
9771         }
9772         this.el.update(html.join(""));
9773         this.nodes = this.el.dom.childNodes;
9774         this.updateIndexes(0);
9775     },
9776
9777     /**
9778      * 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.
9779      * @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:
9780      <pre><code>
9781      view.load({
9782          url: "your-url.php",
9783          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9784          callback: yourFunction,
9785          scope: yourObject, //(optional scope)
9786          discardUrl: false,
9787          nocache: false,
9788          text: "Loading...",
9789          timeout: 30,
9790          scripts: false
9791      });
9792      </code></pre>
9793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9794      * 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.
9795      * @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}
9796      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9797      * @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.
9798      */
9799     load : function(){
9800         var um = this.el.getUpdateManager();
9801         um.update.apply(um, arguments);
9802     },
9803
9804     render : function(el, response){
9805         this.clearSelections();
9806         this.el.update("");
9807         var o;
9808         try{
9809             o = Roo.util.JSON.decode(response.responseText);
9810             if(this.jsonRoot){
9811                 
9812                 o = o[this.jsonRoot];
9813             }
9814         } catch(e){
9815         }
9816         /**
9817          * The current JSON data or null
9818          */
9819         this.jsonData = o;
9820         this.beforeRender();
9821         this.refresh();
9822     },
9823
9824 /**
9825  * Get the number of records in the current JSON dataset
9826  * @return {Number}
9827  */
9828     getCount : function(){
9829         return this.jsonData ? this.jsonData.length : 0;
9830     },
9831
9832 /**
9833  * Returns the JSON object for the specified node(s)
9834  * @param {HTMLElement/Array} node The node or an array of nodes
9835  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9836  * you get the JSON object for the node
9837  */
9838     getNodeData : function(node){
9839         if(node instanceof Array){
9840             var data = [];
9841             for(var i = 0, len = node.length; i < len; i++){
9842                 data.push(this.getNodeData(node[i]));
9843             }
9844             return data;
9845         }
9846         return this.jsonData[this.indexOf(node)] || null;
9847     },
9848
9849     beforeRender : function(){
9850         this.snapshot = this.jsonData;
9851         if(this.sortInfo){
9852             this.sort.apply(this, this.sortInfo);
9853         }
9854         this.fireEvent("beforerender", this, this.jsonData);
9855     },
9856
9857     onLoad : function(el, o){
9858         this.fireEvent("load", this, this.jsonData, o);
9859     },
9860
9861     onLoadException : function(el, o){
9862         this.fireEvent("loadexception", this, o);
9863     },
9864
9865 /**
9866  * Filter the data by a specific property.
9867  * @param {String} property A property on your JSON objects
9868  * @param {String/RegExp} value Either string that the property values
9869  * should start with, or a RegExp to test against the property
9870  */
9871     filter : function(property, value){
9872         if(this.jsonData){
9873             var data = [];
9874             var ss = this.snapshot;
9875             if(typeof value == "string"){
9876                 var vlen = value.length;
9877                 if(vlen == 0){
9878                     this.clearFilter();
9879                     return;
9880                 }
9881                 value = value.toLowerCase();
9882                 for(var i = 0, len = ss.length; i < len; i++){
9883                     var o = ss[i];
9884                     if(o[property].substr(0, vlen).toLowerCase() == value){
9885                         data.push(o);
9886                     }
9887                 }
9888             } else if(value.exec){ // regex?
9889                 for(var i = 0, len = ss.length; i < len; i++){
9890                     var o = ss[i];
9891                     if(value.test(o[property])){
9892                         data.push(o);
9893                     }
9894                 }
9895             } else{
9896                 return;
9897             }
9898             this.jsonData = data;
9899             this.refresh();
9900         }
9901     },
9902
9903 /**
9904  * Filter by a function. The passed function will be called with each
9905  * object in the current dataset. If the function returns true the value is kept,
9906  * otherwise it is filtered.
9907  * @param {Function} fn
9908  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9909  */
9910     filterBy : function(fn, scope){
9911         if(this.jsonData){
9912             var data = [];
9913             var ss = this.snapshot;
9914             for(var i = 0, len = ss.length; i < len; i++){
9915                 var o = ss[i];
9916                 if(fn.call(scope || this, o)){
9917                     data.push(o);
9918                 }
9919             }
9920             this.jsonData = data;
9921             this.refresh();
9922         }
9923     },
9924
9925 /**
9926  * Clears the current filter.
9927  */
9928     clearFilter : function(){
9929         if(this.snapshot && this.jsonData != this.snapshot){
9930             this.jsonData = this.snapshot;
9931             this.refresh();
9932         }
9933     },
9934
9935
9936 /**
9937  * Sorts the data for this view and refreshes it.
9938  * @param {String} property A property on your JSON objects to sort on
9939  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9940  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9941  */
9942     sort : function(property, dir, sortType){
9943         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9944         if(this.jsonData){
9945             var p = property;
9946             var dsc = dir && dir.toLowerCase() == "desc";
9947             var f = function(o1, o2){
9948                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9949                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9950                 ;
9951                 if(v1 < v2){
9952                     return dsc ? +1 : -1;
9953                 } else if(v1 > v2){
9954                     return dsc ? -1 : +1;
9955                 } else{
9956                     return 0;
9957                 }
9958             };
9959             this.jsonData.sort(f);
9960             this.refresh();
9961             if(this.jsonData != this.snapshot){
9962                 this.snapshot.sort(f);
9963             }
9964         }
9965     }
9966 });/*
9967  * Based on:
9968  * Ext JS Library 1.1.1
9969  * Copyright(c) 2006-2007, Ext JS, LLC.
9970  *
9971  * Originally Released Under LGPL - original licence link has changed is not relivant.
9972  *
9973  * Fork - LGPL
9974  * <script type="text/javascript">
9975  */
9976  
9977
9978 /**
9979  * @class Roo.ColorPalette
9980  * @extends Roo.Component
9981  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9982  * Here's an example of typical usage:
9983  * <pre><code>
9984 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9985 cp.render('my-div');
9986
9987 cp.on('select', function(palette, selColor){
9988     // do something with selColor
9989 });
9990 </code></pre>
9991  * @constructor
9992  * Create a new ColorPalette
9993  * @param {Object} config The config object
9994  */
9995 Roo.ColorPalette = function(config){
9996     Roo.ColorPalette.superclass.constructor.call(this, config);
9997     this.addEvents({
9998         /**
9999              * @event select
10000              * Fires when a color is selected
10001              * @param {ColorPalette} this
10002              * @param {String} color The 6-digit color hex code (without the # symbol)
10003              */
10004         select: true
10005     });
10006
10007     if(this.handler){
10008         this.on("select", this.handler, this.scope, true);
10009     }
10010 };
10011 Roo.extend(Roo.ColorPalette, Roo.Component, {
10012     /**
10013      * @cfg {String} itemCls
10014      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10015      */
10016     itemCls : "x-color-palette",
10017     /**
10018      * @cfg {String} value
10019      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10020      * the hex codes are case-sensitive.
10021      */
10022     value : null,
10023     clickEvent:'click',
10024     // private
10025     ctype: "Roo.ColorPalette",
10026
10027     /**
10028      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10029      */
10030     allowReselect : false,
10031
10032     /**
10033      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10034      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10035      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10036      * of colors with the width setting until the box is symmetrical.</p>
10037      * <p>You can override individual colors if needed:</p>
10038      * <pre><code>
10039 var cp = new Roo.ColorPalette();
10040 cp.colors[0] = "FF0000";  // change the first box to red
10041 </code></pre>
10042
10043 Or you can provide a custom array of your own for complete control:
10044 <pre><code>
10045 var cp = new Roo.ColorPalette();
10046 cp.colors = ["000000", "993300", "333300"];
10047 </code></pre>
10048      * @type Array
10049      */
10050     colors : [
10051         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10052         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10053         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10054         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10055         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10056     ],
10057
10058     // private
10059     onRender : function(container, position){
10060         var t = new Roo.MasterTemplate(
10061             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10062         );
10063         var c = this.colors;
10064         for(var i = 0, len = c.length; i < len; i++){
10065             t.add([c[i]]);
10066         }
10067         var el = document.createElement("div");
10068         el.className = this.itemCls;
10069         t.overwrite(el);
10070         container.dom.insertBefore(el, position);
10071         this.el = Roo.get(el);
10072         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10073         if(this.clickEvent != 'click'){
10074             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10075         }
10076     },
10077
10078     // private
10079     afterRender : function(){
10080         Roo.ColorPalette.superclass.afterRender.call(this);
10081         if(this.value){
10082             var s = this.value;
10083             this.value = null;
10084             this.select(s);
10085         }
10086     },
10087
10088     // private
10089     handleClick : function(e, t){
10090         e.preventDefault();
10091         if(!this.disabled){
10092             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10093             this.select(c.toUpperCase());
10094         }
10095     },
10096
10097     /**
10098      * Selects the specified color in the palette (fires the select event)
10099      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10100      */
10101     select : function(color){
10102         color = color.replace("#", "");
10103         if(color != this.value || this.allowReselect){
10104             var el = this.el;
10105             if(this.value){
10106                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10107             }
10108             el.child("a.color-"+color).addClass("x-color-palette-sel");
10109             this.value = color;
10110             this.fireEvent("select", this, color);
10111         }
10112     }
10113 });/*
10114  * Based on:
10115  * Ext JS Library 1.1.1
10116  * Copyright(c) 2006-2007, Ext JS, LLC.
10117  *
10118  * Originally Released Under LGPL - original licence link has changed is not relivant.
10119  *
10120  * Fork - LGPL
10121  * <script type="text/javascript">
10122  */
10123  
10124 /**
10125  * @class Roo.DatePicker
10126  * @extends Roo.Component
10127  * Simple date picker class.
10128  * @constructor
10129  * Create a new DatePicker
10130  * @param {Object} config The config object
10131  */
10132 Roo.DatePicker = function(config){
10133     Roo.DatePicker.superclass.constructor.call(this, config);
10134
10135     this.value = config && config.value ?
10136                  config.value.clearTime() : new Date().clearTime();
10137
10138     this.addEvents({
10139         /**
10140              * @event select
10141              * Fires when a date is selected
10142              * @param {DatePicker} this
10143              * @param {Date} date The selected date
10144              */
10145         'select': true,
10146         /**
10147              * @event monthchange
10148              * Fires when the displayed month changes 
10149              * @param {DatePicker} this
10150              * @param {Date} date The selected month
10151              */
10152         'monthchange': true
10153     });
10154
10155     if(this.handler){
10156         this.on("select", this.handler,  this.scope || this);
10157     }
10158     // build the disabledDatesRE
10159     if(!this.disabledDatesRE && this.disabledDates){
10160         var dd = this.disabledDates;
10161         var re = "(?:";
10162         for(var i = 0; i < dd.length; i++){
10163             re += dd[i];
10164             if(i != dd.length-1) re += "|";
10165         }
10166         this.disabledDatesRE = new RegExp(re + ")");
10167     }
10168 };
10169
10170 Roo.extend(Roo.DatePicker, Roo.Component, {
10171     /**
10172      * @cfg {String} todayText
10173      * The text to display on the button that selects the current date (defaults to "Today")
10174      */
10175     todayText : "Today",
10176     /**
10177      * @cfg {String} okText
10178      * The text to display on the ok button
10179      */
10180     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10181     /**
10182      * @cfg {String} cancelText
10183      * The text to display on the cancel button
10184      */
10185     cancelText : "Cancel",
10186     /**
10187      * @cfg {String} todayTip
10188      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10189      */
10190     todayTip : "{0} (Spacebar)",
10191     /**
10192      * @cfg {Date} minDate
10193      * Minimum allowable date (JavaScript date object, defaults to null)
10194      */
10195     minDate : null,
10196     /**
10197      * @cfg {Date} maxDate
10198      * Maximum allowable date (JavaScript date object, defaults to null)
10199      */
10200     maxDate : null,
10201     /**
10202      * @cfg {String} minText
10203      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10204      */
10205     minText : "This date is before the minimum date",
10206     /**
10207      * @cfg {String} maxText
10208      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10209      */
10210     maxText : "This date is after the maximum date",
10211     /**
10212      * @cfg {String} format
10213      * The default date format string which can be overriden for localization support.  The format must be
10214      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10215      */
10216     format : "m/d/y",
10217     /**
10218      * @cfg {Array} disabledDays
10219      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10220      */
10221     disabledDays : null,
10222     /**
10223      * @cfg {String} disabledDaysText
10224      * The tooltip to display when the date falls on a disabled day (defaults to "")
10225      */
10226     disabledDaysText : "",
10227     /**
10228      * @cfg {RegExp} disabledDatesRE
10229      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10230      */
10231     disabledDatesRE : null,
10232     /**
10233      * @cfg {String} disabledDatesText
10234      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10235      */
10236     disabledDatesText : "",
10237     /**
10238      * @cfg {Boolean} constrainToViewport
10239      * True to constrain the date picker to the viewport (defaults to true)
10240      */
10241     constrainToViewport : true,
10242     /**
10243      * @cfg {Array} monthNames
10244      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10245      */
10246     monthNames : Date.monthNames,
10247     /**
10248      * @cfg {Array} dayNames
10249      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10250      */
10251     dayNames : Date.dayNames,
10252     /**
10253      * @cfg {String} nextText
10254      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10255      */
10256     nextText: 'Next Month (Control+Right)',
10257     /**
10258      * @cfg {String} prevText
10259      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10260      */
10261     prevText: 'Previous Month (Control+Left)',
10262     /**
10263      * @cfg {String} monthYearText
10264      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10265      */
10266     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10267     /**
10268      * @cfg {Number} startDay
10269      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10270      */
10271     startDay : 0,
10272     /**
10273      * @cfg {Bool} showClear
10274      * Show a clear button (usefull for date form elements that can be blank.)
10275      */
10276     
10277     showClear: false,
10278     
10279     /**
10280      * Sets the value of the date field
10281      * @param {Date} value The date to set
10282      */
10283     setValue : function(value){
10284         var old = this.value;
10285         this.value = value.clearTime(true);
10286         if(this.el){
10287             this.update(this.value);
10288         }
10289     },
10290
10291     /**
10292      * Gets the current selected value of the date field
10293      * @return {Date} The selected date
10294      */
10295     getValue : function(){
10296         return this.value;
10297     },
10298
10299     // private
10300     focus : function(){
10301         if(this.el){
10302             this.update(this.activeDate);
10303         }
10304     },
10305
10306     // private
10307     onRender : function(container, position){
10308         var m = [
10309              '<table cellspacing="0">',
10310                 '<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>',
10311                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10312         var dn = this.dayNames;
10313         for(var i = 0; i < 7; i++){
10314             var d = this.startDay+i;
10315             if(d > 6){
10316                 d = d-7;
10317             }
10318             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10319         }
10320         m[m.length] = "</tr></thead><tbody><tr>";
10321         for(var i = 0; i < 42; i++) {
10322             if(i % 7 == 0 && i != 0){
10323                 m[m.length] = "</tr><tr>";
10324             }
10325             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10326         }
10327         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10328             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10329
10330         var el = document.createElement("div");
10331         el.className = "x-date-picker";
10332         el.innerHTML = m.join("");
10333
10334         container.dom.insertBefore(el, position);
10335
10336         this.el = Roo.get(el);
10337         this.eventEl = Roo.get(el.firstChild);
10338
10339         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10340             handler: this.showPrevMonth,
10341             scope: this,
10342             preventDefault:true,
10343             stopDefault:true
10344         });
10345
10346         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10347             handler: this.showNextMonth,
10348             scope: this,
10349             preventDefault:true,
10350             stopDefault:true
10351         });
10352
10353         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10354
10355         this.monthPicker = this.el.down('div.x-date-mp');
10356         this.monthPicker.enableDisplayMode('block');
10357         
10358         var kn = new Roo.KeyNav(this.eventEl, {
10359             "left" : function(e){
10360                 e.ctrlKey ?
10361                     this.showPrevMonth() :
10362                     this.update(this.activeDate.add("d", -1));
10363             },
10364
10365             "right" : function(e){
10366                 e.ctrlKey ?
10367                     this.showNextMonth() :
10368                     this.update(this.activeDate.add("d", 1));
10369             },
10370
10371             "up" : function(e){
10372                 e.ctrlKey ?
10373                     this.showNextYear() :
10374                     this.update(this.activeDate.add("d", -7));
10375             },
10376
10377             "down" : function(e){
10378                 e.ctrlKey ?
10379                     this.showPrevYear() :
10380                     this.update(this.activeDate.add("d", 7));
10381             },
10382
10383             "pageUp" : function(e){
10384                 this.showNextMonth();
10385             },
10386
10387             "pageDown" : function(e){
10388                 this.showPrevMonth();
10389             },
10390
10391             "enter" : function(e){
10392                 e.stopPropagation();
10393                 return true;
10394             },
10395
10396             scope : this
10397         });
10398
10399         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10400
10401         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10402
10403         this.el.unselectable();
10404         
10405         this.cells = this.el.select("table.x-date-inner tbody td");
10406         this.textNodes = this.el.query("table.x-date-inner tbody span");
10407
10408         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10409             text: "&#160;",
10410             tooltip: this.monthYearText
10411         });
10412
10413         this.mbtn.on('click', this.showMonthPicker, this);
10414         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10415
10416
10417         var today = (new Date()).dateFormat(this.format);
10418         
10419         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10420         if (this.showClear) {
10421             baseTb.add( new Roo.Toolbar.Fill());
10422         }
10423         baseTb.add({
10424             text: String.format(this.todayText, today),
10425             tooltip: String.format(this.todayTip, today),
10426             handler: this.selectToday,
10427             scope: this
10428         });
10429         
10430         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10431             
10432         //});
10433         if (this.showClear) {
10434             
10435             baseTb.add( new Roo.Toolbar.Fill());
10436             baseTb.add({
10437                 text: '&#160;',
10438                 cls: 'x-btn-icon x-btn-clear',
10439                 handler: function() {
10440                     //this.value = '';
10441                     this.fireEvent("select", this, '');
10442                 },
10443                 scope: this
10444             });
10445         }
10446         
10447         
10448         if(Roo.isIE){
10449             this.el.repaint();
10450         }
10451         this.update(this.value);
10452     },
10453
10454     createMonthPicker : function(){
10455         if(!this.monthPicker.dom.firstChild){
10456             var buf = ['<table border="0" cellspacing="0">'];
10457             for(var i = 0; i < 6; i++){
10458                 buf.push(
10459                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10460                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10461                     i == 0 ?
10462                     '<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>' :
10463                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10464                 );
10465             }
10466             buf.push(
10467                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10468                     this.okText,
10469                     '</button><button type="button" class="x-date-mp-cancel">',
10470                     this.cancelText,
10471                     '</button></td></tr>',
10472                 '</table>'
10473             );
10474             this.monthPicker.update(buf.join(''));
10475             this.monthPicker.on('click', this.onMonthClick, this);
10476             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10477
10478             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10479             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10480
10481             this.mpMonths.each(function(m, a, i){
10482                 i += 1;
10483                 if((i%2) == 0){
10484                     m.dom.xmonth = 5 + Math.round(i * .5);
10485                 }else{
10486                     m.dom.xmonth = Math.round((i-1) * .5);
10487                 }
10488             });
10489         }
10490     },
10491
10492     showMonthPicker : function(){
10493         this.createMonthPicker();
10494         var size = this.el.getSize();
10495         this.monthPicker.setSize(size);
10496         this.monthPicker.child('table').setSize(size);
10497
10498         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10499         this.updateMPMonth(this.mpSelMonth);
10500         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10501         this.updateMPYear(this.mpSelYear);
10502
10503         this.monthPicker.slideIn('t', {duration:.2});
10504     },
10505
10506     updateMPYear : function(y){
10507         this.mpyear = y;
10508         var ys = this.mpYears.elements;
10509         for(var i = 1; i <= 10; i++){
10510             var td = ys[i-1], y2;
10511             if((i%2) == 0){
10512                 y2 = y + Math.round(i * .5);
10513                 td.firstChild.innerHTML = y2;
10514                 td.xyear = y2;
10515             }else{
10516                 y2 = y - (5-Math.round(i * .5));
10517                 td.firstChild.innerHTML = y2;
10518                 td.xyear = y2;
10519             }
10520             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10521         }
10522     },
10523
10524     updateMPMonth : function(sm){
10525         this.mpMonths.each(function(m, a, i){
10526             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10527         });
10528     },
10529
10530     selectMPMonth: function(m){
10531         
10532     },
10533
10534     onMonthClick : function(e, t){
10535         e.stopEvent();
10536         var el = new Roo.Element(t), pn;
10537         if(el.is('button.x-date-mp-cancel')){
10538             this.hideMonthPicker();
10539         }
10540         else if(el.is('button.x-date-mp-ok')){
10541             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10542             this.hideMonthPicker();
10543         }
10544         else if(pn = el.up('td.x-date-mp-month', 2)){
10545             this.mpMonths.removeClass('x-date-mp-sel');
10546             pn.addClass('x-date-mp-sel');
10547             this.mpSelMonth = pn.dom.xmonth;
10548         }
10549         else if(pn = el.up('td.x-date-mp-year', 2)){
10550             this.mpYears.removeClass('x-date-mp-sel');
10551             pn.addClass('x-date-mp-sel');
10552             this.mpSelYear = pn.dom.xyear;
10553         }
10554         else if(el.is('a.x-date-mp-prev')){
10555             this.updateMPYear(this.mpyear-10);
10556         }
10557         else if(el.is('a.x-date-mp-next')){
10558             this.updateMPYear(this.mpyear+10);
10559         }
10560     },
10561
10562     onMonthDblClick : function(e, t){
10563         e.stopEvent();
10564         var el = new Roo.Element(t), pn;
10565         if(pn = el.up('td.x-date-mp-month', 2)){
10566             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10567             this.hideMonthPicker();
10568         }
10569         else if(pn = el.up('td.x-date-mp-year', 2)){
10570             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10571             this.hideMonthPicker();
10572         }
10573     },
10574
10575     hideMonthPicker : function(disableAnim){
10576         if(this.monthPicker){
10577             if(disableAnim === true){
10578                 this.monthPicker.hide();
10579             }else{
10580                 this.monthPicker.slideOut('t', {duration:.2});
10581             }
10582         }
10583     },
10584
10585     // private
10586     showPrevMonth : function(e){
10587         this.update(this.activeDate.add("mo", -1));
10588     },
10589
10590     // private
10591     showNextMonth : function(e){
10592         this.update(this.activeDate.add("mo", 1));
10593     },
10594
10595     // private
10596     showPrevYear : function(){
10597         this.update(this.activeDate.add("y", -1));
10598     },
10599
10600     // private
10601     showNextYear : function(){
10602         this.update(this.activeDate.add("y", 1));
10603     },
10604
10605     // private
10606     handleMouseWheel : function(e){
10607         var delta = e.getWheelDelta();
10608         if(delta > 0){
10609             this.showPrevMonth();
10610             e.stopEvent();
10611         } else if(delta < 0){
10612             this.showNextMonth();
10613             e.stopEvent();
10614         }
10615     },
10616
10617     // private
10618     handleDateClick : function(e, t){
10619         e.stopEvent();
10620         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10621             this.setValue(new Date(t.dateValue));
10622             this.fireEvent("select", this, this.value);
10623         }
10624     },
10625
10626     // private
10627     selectToday : function(){
10628         this.setValue(new Date().clearTime());
10629         this.fireEvent("select", this, this.value);
10630     },
10631
10632     // private
10633     update : function(date)
10634     {
10635         var vd = this.activeDate;
10636         this.activeDate = date;
10637         if(vd && this.el){
10638             var t = date.getTime();
10639             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10640                 this.cells.removeClass("x-date-selected");
10641                 this.cells.each(function(c){
10642                    if(c.dom.firstChild.dateValue == t){
10643                        c.addClass("x-date-selected");
10644                        setTimeout(function(){
10645                             try{c.dom.firstChild.focus();}catch(e){}
10646                        }, 50);
10647                        return false;
10648                    }
10649                 });
10650                 return;
10651             }
10652         }
10653         
10654         var days = date.getDaysInMonth();
10655         var firstOfMonth = date.getFirstDateOfMonth();
10656         var startingPos = firstOfMonth.getDay()-this.startDay;
10657
10658         if(startingPos <= this.startDay){
10659             startingPos += 7;
10660         }
10661
10662         var pm = date.add("mo", -1);
10663         var prevStart = pm.getDaysInMonth()-startingPos;
10664
10665         var cells = this.cells.elements;
10666         var textEls = this.textNodes;
10667         days += startingPos;
10668
10669         // convert everything to numbers so it's fast
10670         var day = 86400000;
10671         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10672         var today = new Date().clearTime().getTime();
10673         var sel = date.clearTime().getTime();
10674         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10675         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10676         var ddMatch = this.disabledDatesRE;
10677         var ddText = this.disabledDatesText;
10678         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10679         var ddaysText = this.disabledDaysText;
10680         var format = this.format;
10681
10682         var setCellClass = function(cal, cell){
10683             cell.title = "";
10684             var t = d.getTime();
10685             cell.firstChild.dateValue = t;
10686             if(t == today){
10687                 cell.className += " x-date-today";
10688                 cell.title = cal.todayText;
10689             }
10690             if(t == sel){
10691                 cell.className += " x-date-selected";
10692                 setTimeout(function(){
10693                     try{cell.firstChild.focus();}catch(e){}
10694                 }, 50);
10695             }
10696             // disabling
10697             if(t < min) {
10698                 cell.className = " x-date-disabled";
10699                 cell.title = cal.minText;
10700                 return;
10701             }
10702             if(t > max) {
10703                 cell.className = " x-date-disabled";
10704                 cell.title = cal.maxText;
10705                 return;
10706             }
10707             if(ddays){
10708                 if(ddays.indexOf(d.getDay()) != -1){
10709                     cell.title = ddaysText;
10710                     cell.className = " x-date-disabled";
10711                 }
10712             }
10713             if(ddMatch && format){
10714                 var fvalue = d.dateFormat(format);
10715                 if(ddMatch.test(fvalue)){
10716                     cell.title = ddText.replace("%0", fvalue);
10717                     cell.className = " x-date-disabled";
10718                 }
10719             }
10720         };
10721
10722         var i = 0;
10723         for(; i < startingPos; i++) {
10724             textEls[i].innerHTML = (++prevStart);
10725             d.setDate(d.getDate()+1);
10726             cells[i].className = "x-date-prevday";
10727             setCellClass(this, cells[i]);
10728         }
10729         for(; i < days; i++){
10730             intDay = i - startingPos + 1;
10731             textEls[i].innerHTML = (intDay);
10732             d.setDate(d.getDate()+1);
10733             cells[i].className = "x-date-active";
10734             setCellClass(this, cells[i]);
10735         }
10736         var extraDays = 0;
10737         for(; i < 42; i++) {
10738              textEls[i].innerHTML = (++extraDays);
10739              d.setDate(d.getDate()+1);
10740              cells[i].className = "x-date-nextday";
10741              setCellClass(this, cells[i]);
10742         }
10743
10744         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10745         this.fireEvent('monthchange', this, date);
10746         
10747         if(!this.internalRender){
10748             var main = this.el.dom.firstChild;
10749             var w = main.offsetWidth;
10750             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10751             Roo.fly(main).setWidth(w);
10752             this.internalRender = true;
10753             // opera does not respect the auto grow header center column
10754             // then, after it gets a width opera refuses to recalculate
10755             // without a second pass
10756             if(Roo.isOpera && !this.secondPass){
10757                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10758                 this.secondPass = true;
10759                 this.update.defer(10, this, [date]);
10760             }
10761         }
10762         
10763         
10764     }
10765 });        /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775 /**
10776  * @class Roo.TabPanel
10777  * @extends Roo.util.Observable
10778  * A lightweight tab container.
10779  * <br><br>
10780  * Usage:
10781  * <pre><code>
10782 // basic tabs 1, built from existing content
10783 var tabs = new Roo.TabPanel("tabs1");
10784 tabs.addTab("script", "View Script");
10785 tabs.addTab("markup", "View Markup");
10786 tabs.activate("script");
10787
10788 // more advanced tabs, built from javascript
10789 var jtabs = new Roo.TabPanel("jtabs");
10790 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10791
10792 // set up the UpdateManager
10793 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10794 var updater = tab2.getUpdateManager();
10795 updater.setDefaultUrl("ajax1.htm");
10796 tab2.on('activate', updater.refresh, updater, true);
10797
10798 // Use setUrl for Ajax loading
10799 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10800 tab3.setUrl("ajax2.htm", null, true);
10801
10802 // Disabled tab
10803 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10804 tab4.disable();
10805
10806 jtabs.activate("jtabs-1");
10807  * </code></pre>
10808  * @constructor
10809  * Create a new TabPanel.
10810  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10811  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10812  */
10813 Roo.TabPanel = function(container, config){
10814     /**
10815     * The container element for this TabPanel.
10816     * @type Roo.Element
10817     */
10818     this.el = Roo.get(container, true);
10819     if(config){
10820         if(typeof config == "boolean"){
10821             this.tabPosition = config ? "bottom" : "top";
10822         }else{
10823             Roo.apply(this, config);
10824         }
10825     }
10826     if(this.tabPosition == "bottom"){
10827         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10828         this.el.addClass("x-tabs-bottom");
10829     }
10830     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10831     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10832     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10833     if(Roo.isIE){
10834         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10835     }
10836     if(this.tabPosition != "bottom"){
10837         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10838          * @type Roo.Element
10839          */
10840         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10841         this.el.addClass("x-tabs-top");
10842     }
10843     this.items = [];
10844
10845     this.bodyEl.setStyle("position", "relative");
10846
10847     this.active = null;
10848     this.activateDelegate = this.activate.createDelegate(this);
10849
10850     this.addEvents({
10851         /**
10852          * @event tabchange
10853          * Fires when the active tab changes
10854          * @param {Roo.TabPanel} this
10855          * @param {Roo.TabPanelItem} activePanel The new active tab
10856          */
10857         "tabchange": true,
10858         /**
10859          * @event beforetabchange
10860          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10861          * @param {Roo.TabPanel} this
10862          * @param {Object} e Set cancel to true on this object to cancel the tab change
10863          * @param {Roo.TabPanelItem} tab The tab being changed to
10864          */
10865         "beforetabchange" : true
10866     });
10867
10868     Roo.EventManager.onWindowResize(this.onResize, this);
10869     this.cpad = this.el.getPadding("lr");
10870     this.hiddenCount = 0;
10871
10872
10873     // toolbar on the tabbar support...
10874     if (this.toolbar) {
10875         var tcfg = this.toolbar;
10876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10877         this.toolbar = new Roo.Toolbar(tcfg);
10878         if (Roo.isSafari) {
10879             var tbl = tcfg.container.child('table', true);
10880             tbl.setAttribute('width', '100%');
10881         }
10882         
10883     }
10884    
10885
10886
10887     Roo.TabPanel.superclass.constructor.call(this);
10888 };
10889
10890 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10891     /*
10892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10893      */
10894     tabPosition : "top",
10895     /*
10896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10897      */
10898     currentTabWidth : 0,
10899     /*
10900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10901      */
10902     minTabWidth : 40,
10903     /*
10904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10905      */
10906     maxTabWidth : 250,
10907     /*
10908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10909      */
10910     preferredTabWidth : 175,
10911     /*
10912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10913      */
10914     resizeTabs : false,
10915     /*
10916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10917      */
10918     monitorResize : true,
10919     /*
10920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10921      */
10922     toolbar : false,
10923
10924     /**
10925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10926      * @param {String} id The id of the div to use <b>or create</b>
10927      * @param {String} text The text for the tab
10928      * @param {String} content (optional) Content to put in the TabPanelItem body
10929      * @param {Boolean} closable (optional) True to create a close icon on the tab
10930      * @return {Roo.TabPanelItem} The created TabPanelItem
10931      */
10932     addTab : function(id, text, content, closable){
10933         var item = new Roo.TabPanelItem(this, id, text, closable);
10934         this.addTabItem(item);
10935         if(content){
10936             item.setContent(content);
10937         }
10938         return item;
10939     },
10940
10941     /**
10942      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10943      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10944      * @return {Roo.TabPanelItem}
10945      */
10946     getTab : function(id){
10947         return this.items[id];
10948     },
10949
10950     /**
10951      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10952      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10953      */
10954     hideTab : function(id){
10955         var t = this.items[id];
10956         if(!t.isHidden()){
10957            t.setHidden(true);
10958            this.hiddenCount++;
10959            this.autoSizeTabs();
10960         }
10961     },
10962
10963     /**
10964      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10965      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10966      */
10967     unhideTab : function(id){
10968         var t = this.items[id];
10969         if(t.isHidden()){
10970            t.setHidden(false);
10971            this.hiddenCount--;
10972            this.autoSizeTabs();
10973         }
10974     },
10975
10976     /**
10977      * Adds an existing {@link Roo.TabPanelItem}.
10978      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10979      */
10980     addTabItem : function(item){
10981         this.items[item.id] = item;
10982         this.items.push(item);
10983         if(this.resizeTabs){
10984            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10985            this.autoSizeTabs();
10986         }else{
10987             item.autoSize();
10988         }
10989     },
10990
10991     /**
10992      * Removes a {@link Roo.TabPanelItem}.
10993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10994      */
10995     removeTab : function(id){
10996         var items = this.items;
10997         var tab = items[id];
10998         if(!tab) { return; }
10999         var index = items.indexOf(tab);
11000         if(this.active == tab && items.length > 1){
11001             var newTab = this.getNextAvailable(index);
11002             if(newTab) {
11003                 newTab.activate();
11004             }
11005         }
11006         this.stripEl.dom.removeChild(tab.pnode.dom);
11007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11009         }
11010         items.splice(index, 1);
11011         delete this.items[tab.id];
11012         tab.fireEvent("close", tab);
11013         tab.purgeListeners();
11014         this.autoSizeTabs();
11015     },
11016
11017     getNextAvailable : function(start){
11018         var items = this.items;
11019         var index = start;
11020         // look for a next tab that will slide over to
11021         // replace the one being removed
11022         while(index < items.length){
11023             var item = items[++index];
11024             if(item && !item.isHidden()){
11025                 return item;
11026             }
11027         }
11028         // if one isn't found select the previous tab (on the left)
11029         index = start;
11030         while(index >= 0){
11031             var item = items[--index];
11032             if(item && !item.isHidden()){
11033                 return item;
11034             }
11035         }
11036         return null;
11037     },
11038
11039     /**
11040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11042      */
11043     disableTab : function(id){
11044         var tab = this.items[id];
11045         if(tab && this.active != tab){
11046             tab.disable();
11047         }
11048     },
11049
11050     /**
11051      * Enables a {@link Roo.TabPanelItem} that is disabled.
11052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11053      */
11054     enableTab : function(id){
11055         var tab = this.items[id];
11056         tab.enable();
11057     },
11058
11059     /**
11060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11062      * @return {Roo.TabPanelItem} The TabPanelItem.
11063      */
11064     activate : function(id){
11065         var tab = this.items[id];
11066         if(!tab){
11067             return null;
11068         }
11069         if(tab == this.active || tab.disabled){
11070             return tab;
11071         }
11072         var e = {};
11073         this.fireEvent("beforetabchange", this, e, tab);
11074         if(e.cancel !== true && !tab.disabled){
11075             if(this.active){
11076                 this.active.hide();
11077             }
11078             this.active = this.items[id];
11079             this.active.show();
11080             this.fireEvent("tabchange", this, this.active);
11081         }
11082         return tab;
11083     },
11084
11085     /**
11086      * Gets the active {@link Roo.TabPanelItem}.
11087      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11088      */
11089     getActiveTab : function(){
11090         return this.active;
11091     },
11092
11093     /**
11094      * Updates the tab body element to fit the height of the container element
11095      * for overflow scrolling
11096      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11097      */
11098     syncHeight : function(targetHeight){
11099         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11100         var bm = this.bodyEl.getMargins();
11101         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11102         this.bodyEl.setHeight(newHeight);
11103         return newHeight;
11104     },
11105
11106     onResize : function(){
11107         if(this.monitorResize){
11108             this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11114      */
11115     beginUpdate : function(){
11116         this.updating = true;
11117     },
11118
11119     /**
11120      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11121      */
11122     endUpdate : function(){
11123         this.updating = false;
11124         this.autoSizeTabs();
11125     },
11126
11127     /**
11128      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11129      */
11130     autoSizeTabs : function(){
11131         var count = this.items.length;
11132         var vcount = count - this.hiddenCount;
11133         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11134         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11135         var availWidth = Math.floor(w / vcount);
11136         var b = this.stripBody;
11137         if(b.getWidth() > w){
11138             var tabs = this.items;
11139             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11140             if(availWidth < this.minTabWidth){
11141                 /*if(!this.sleft){    // incomplete scrolling code
11142                     this.createScrollButtons();
11143                 }
11144                 this.showScroll();
11145                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11146             }
11147         }else{
11148             if(this.currentTabWidth < this.preferredTabWidth){
11149                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11150             }
11151         }
11152     },
11153
11154     /**
11155      * Returns the number of tabs in this TabPanel.
11156      * @return {Number}
11157      */
11158      getCount : function(){
11159          return this.items.length;
11160      },
11161
11162     /**
11163      * Resizes all the tabs to the passed width
11164      * @param {Number} The new width
11165      */
11166     setTabWidth : function(width){
11167         this.currentTabWidth = width;
11168         for(var i = 0, len = this.items.length; i < len; i++) {
11169                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11170         }
11171     },
11172
11173     /**
11174      * Destroys this TabPanel
11175      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11176      */
11177     destroy : function(removeEl){
11178         Roo.EventManager.removeResizeListener(this.onResize, this);
11179         for(var i = 0, len = this.items.length; i < len; i++){
11180             this.items[i].purgeListeners();
11181         }
11182         if(removeEl === true){
11183             this.el.update("");
11184             this.el.remove();
11185         }
11186     }
11187 });
11188
11189 /**
11190  * @class Roo.TabPanelItem
11191  * @extends Roo.util.Observable
11192  * Represents an individual item (tab plus body) in a TabPanel.
11193  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11194  * @param {String} id The id of this TabPanelItem
11195  * @param {String} text The text for the tab of this TabPanelItem
11196  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11197  */
11198 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11199     /**
11200      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11201      * @type Roo.TabPanel
11202      */
11203     this.tabPanel = tabPanel;
11204     /**
11205      * The id for this TabPanelItem
11206      * @type String
11207      */
11208     this.id = id;
11209     /** @private */
11210     this.disabled = false;
11211     /** @private */
11212     this.text = text;
11213     /** @private */
11214     this.loaded = false;
11215     this.closable = closable;
11216
11217     /**
11218      * The body element for this TabPanelItem.
11219      * @type Roo.Element
11220      */
11221     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11222     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11223     this.bodyEl.setStyle("display", "block");
11224     this.bodyEl.setStyle("zoom", "1");
11225     this.hideAction();
11226
11227     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11228     /** @private */
11229     this.el = Roo.get(els.el, true);
11230     this.inner = Roo.get(els.inner, true);
11231     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11232     this.pnode = Roo.get(els.el.parentNode, true);
11233     this.el.on("mousedown", this.onTabMouseDown, this);
11234     this.el.on("click", this.onTabClick, this);
11235     /** @private */
11236     if(closable){
11237         var c = Roo.get(els.close, true);
11238         c.dom.title = this.closeText;
11239         c.addClassOnOver("close-over");
11240         c.on("click", this.closeClick, this);
11241      }
11242
11243     this.addEvents({
11244          /**
11245          * @event activate
11246          * Fires when this tab becomes the active tab.
11247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11248          * @param {Roo.TabPanelItem} this
11249          */
11250         "activate": true,
11251         /**
11252          * @event beforeclose
11253          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11254          * @param {Roo.TabPanelItem} this
11255          * @param {Object} e Set cancel to true on this object to cancel the close.
11256          */
11257         "beforeclose": true,
11258         /**
11259          * @event close
11260          * Fires when this tab is closed.
11261          * @param {Roo.TabPanelItem} this
11262          */
11263          "close": true,
11264         /**
11265          * @event deactivate
11266          * Fires when this tab is no longer the active tab.
11267          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11268          * @param {Roo.TabPanelItem} this
11269          */
11270          "deactivate" : true
11271     });
11272     this.hidden = false;
11273
11274     Roo.TabPanelItem.superclass.constructor.call(this);
11275 };
11276
11277 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11278     purgeListeners : function(){
11279        Roo.util.Observable.prototype.purgeListeners.call(this);
11280        this.el.removeAllListeners();
11281     },
11282     /**
11283      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11284      */
11285     show : function(){
11286         this.pnode.addClass("on");
11287         this.showAction();
11288         if(Roo.isOpera){
11289             this.tabPanel.stripWrap.repaint();
11290         }
11291         this.fireEvent("activate", this.tabPanel, this);
11292     },
11293
11294     /**
11295      * Returns true if this tab is the active tab.
11296      * @return {Boolean}
11297      */
11298     isActive : function(){
11299         return this.tabPanel.getActiveTab() == this;
11300     },
11301
11302     /**
11303      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11304      */
11305     hide : function(){
11306         this.pnode.removeClass("on");
11307         this.hideAction();
11308         this.fireEvent("deactivate", this.tabPanel, this);
11309     },
11310
11311     hideAction : function(){
11312         this.bodyEl.hide();
11313         this.bodyEl.setStyle("position", "absolute");
11314         this.bodyEl.setLeft("-20000px");
11315         this.bodyEl.setTop("-20000px");
11316     },
11317
11318     showAction : function(){
11319         this.bodyEl.setStyle("position", "relative");
11320         this.bodyEl.setTop("");
11321         this.bodyEl.setLeft("");
11322         this.bodyEl.show();
11323     },
11324
11325     /**
11326      * Set the tooltip for the tab.
11327      * @param {String} tooltip The tab's tooltip
11328      */
11329     setTooltip : function(text){
11330         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11331             this.textEl.dom.qtip = text;
11332             this.textEl.dom.removeAttribute('title');
11333         }else{
11334             this.textEl.dom.title = text;
11335         }
11336     },
11337
11338     onTabClick : function(e){
11339         e.preventDefault();
11340         this.tabPanel.activate(this.id);
11341     },
11342
11343     onTabMouseDown : function(e){
11344         e.preventDefault();
11345         this.tabPanel.activate(this.id);
11346     },
11347
11348     getWidth : function(){
11349         return this.inner.getWidth();
11350     },
11351
11352     setWidth : function(width){
11353         var iwidth = width - this.pnode.getPadding("lr");
11354         this.inner.setWidth(iwidth);
11355         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11356         this.pnode.setWidth(width);
11357     },
11358
11359     /**
11360      * Show or hide the tab
11361      * @param {Boolean} hidden True to hide or false to show.
11362      */
11363     setHidden : function(hidden){
11364         this.hidden = hidden;
11365         this.pnode.setStyle("display", hidden ? "none" : "");
11366     },
11367
11368     /**
11369      * Returns true if this tab is "hidden"
11370      * @return {Boolean}
11371      */
11372     isHidden : function(){
11373         return this.hidden;
11374     },
11375
11376     /**
11377      * Returns the text for this tab
11378      * @return {String}
11379      */
11380     getText : function(){
11381         return this.text;
11382     },
11383
11384     autoSize : function(){
11385         //this.el.beginMeasure();
11386         this.textEl.setWidth(1);
11387         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11388         //this.el.endMeasure();
11389     },
11390
11391     /**
11392      * Sets the text for the tab (Note: this also sets the tooltip text)
11393      * @param {String} text The tab's text and tooltip
11394      */
11395     setText : function(text){
11396         this.text = text;
11397         this.textEl.update(text);
11398         this.setTooltip(text);
11399         if(!this.tabPanel.resizeTabs){
11400             this.autoSize();
11401         }
11402     },
11403     /**
11404      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11405      */
11406     activate : function(){
11407         this.tabPanel.activate(this.id);
11408     },
11409
11410     /**
11411      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11412      */
11413     disable : function(){
11414         if(this.tabPanel.active != this){
11415             this.disabled = true;
11416             this.pnode.addClass("disabled");
11417         }
11418     },
11419
11420     /**
11421      * Enables this TabPanelItem if it was previously disabled.
11422      */
11423     enable : function(){
11424         this.disabled = false;
11425         this.pnode.removeClass("disabled");
11426     },
11427
11428     /**
11429      * Sets the content for this TabPanelItem.
11430      * @param {String} content The content
11431      * @param {Boolean} loadScripts true to look for and load scripts
11432      */
11433     setContent : function(content, loadScripts){
11434         this.bodyEl.update(content, loadScripts);
11435     },
11436
11437     /**
11438      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11439      * @return {Roo.UpdateManager} The UpdateManager
11440      */
11441     getUpdateManager : function(){
11442         return this.bodyEl.getUpdateManager();
11443     },
11444
11445     /**
11446      * Set a URL to be used to load the content for this TabPanelItem.
11447      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11448      * @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)
11449      * @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)
11450      * @return {Roo.UpdateManager} The UpdateManager
11451      */
11452     setUrl : function(url, params, loadOnce){
11453         if(this.refreshDelegate){
11454             this.un('activate', this.refreshDelegate);
11455         }
11456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11457         this.on("activate", this.refreshDelegate);
11458         return this.bodyEl.getUpdateManager();
11459     },
11460
11461     /** @private */
11462     _handleRefresh : function(url, params, loadOnce){
11463         if(!loadOnce || !this.loaded){
11464             var updater = this.bodyEl.getUpdateManager();
11465             updater.update(url, params, this._setLoaded.createDelegate(this));
11466         }
11467     },
11468
11469     /**
11470      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11471      *   Will fail silently if the setUrl method has not been called.
11472      *   This does not activate the panel, just updates its content.
11473      */
11474     refresh : function(){
11475         if(this.refreshDelegate){
11476            this.loaded = false;
11477            this.refreshDelegate();
11478         }
11479     },
11480
11481     /** @private */
11482     _setLoaded : function(){
11483         this.loaded = true;
11484     },
11485
11486     /** @private */
11487     closeClick : function(e){
11488         var o = {};
11489         e.stopEvent();
11490         this.fireEvent("beforeclose", this, o);
11491         if(o.cancel !== true){
11492             this.tabPanel.removeTab(this.id);
11493         }
11494     },
11495     /**
11496      * The text displayed in the tooltip for the close icon.
11497      * @type String
11498      */
11499     closeText : "Close this tab"
11500 });
11501
11502 /** @private */
11503 Roo.TabPanel.prototype.createStrip = function(container){
11504     var strip = document.createElement("div");
11505     strip.className = "x-tabs-wrap";
11506     container.appendChild(strip);
11507     return strip;
11508 };
11509 /** @private */
11510 Roo.TabPanel.prototype.createStripList = function(strip){
11511     // div wrapper for retard IE
11512     // returns the "tr" element.
11513     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11514         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11515         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11516     return strip.firstChild.firstChild.firstChild.firstChild;
11517 };
11518 /** @private */
11519 Roo.TabPanel.prototype.createBody = function(container){
11520     var body = document.createElement("div");
11521     Roo.id(body, "tab-body");
11522     Roo.fly(body).addClass("x-tabs-body");
11523     container.appendChild(body);
11524     return body;
11525 };
11526 /** @private */
11527 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11528     var body = Roo.getDom(id);
11529     if(!body){
11530         body = document.createElement("div");
11531         body.id = id;
11532     }
11533     Roo.fly(body).addClass("x-tabs-item-body");
11534     bodyEl.insertBefore(body, bodyEl.firstChild);
11535     return body;
11536 };
11537 /** @private */
11538 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11539     var td = document.createElement("td");
11540     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11541     //stripEl.appendChild(td);
11542     if(closable){
11543         td.className = "x-tabs-closable";
11544         if(!this.closeTpl){
11545             this.closeTpl = new Roo.Template(
11546                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11547                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11548                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11549             );
11550         }
11551         var el = this.closeTpl.overwrite(td, {"text": text});
11552         var close = el.getElementsByTagName("div")[0];
11553         var inner = el.getElementsByTagName("em")[0];
11554         return {"el": el, "close": close, "inner": inner};
11555     } else {
11556         if(!this.tabTpl){
11557             this.tabTpl = new Roo.Template(
11558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11560             );
11561         }
11562         var el = this.tabTpl.overwrite(td, {"text": text});
11563         var inner = el.getElementsByTagName("em")[0];
11564         return {"el": el, "inner": inner};
11565     }
11566 };/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576
11577 /**
11578  * @class Roo.Button
11579  * @extends Roo.util.Observable
11580  * Simple Button class
11581  * @cfg {String} text The button text
11582  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11583  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11584  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11585  * @cfg {Object} scope The scope of the handler
11586  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11587  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11588  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11589  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11590  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11591  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11592    applies if enableToggle = true)
11593  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11594  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11595   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11596  * @constructor
11597  * Create a new button
11598  * @param {Object} config The config object
11599  */
11600 Roo.Button = function(renderTo, config)
11601 {
11602     if (!config) {
11603         config = renderTo;
11604         renderTo = config.renderTo || false;
11605     }
11606     
11607     Roo.apply(this, config);
11608     this.addEvents({
11609         /**
11610              * @event click
11611              * Fires when this button is clicked
11612              * @param {Button} this
11613              * @param {EventObject} e The click event
11614              */
11615             "click" : true,
11616         /**
11617              * @event toggle
11618              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11619              * @param {Button} this
11620              * @param {Boolean} pressed
11621              */
11622             "toggle" : true,
11623         /**
11624              * @event mouseover
11625              * Fires when the mouse hovers over the button
11626              * @param {Button} this
11627              * @param {Event} e The event object
11628              */
11629         'mouseover' : true,
11630         /**
11631              * @event mouseout
11632              * Fires when the mouse exits the button
11633              * @param {Button} this
11634              * @param {Event} e The event object
11635              */
11636         'mouseout': true,
11637          /**
11638              * @event render
11639              * Fires when the button is rendered
11640              * @param {Button} this
11641              */
11642         'render': true
11643     });
11644     if(this.menu){
11645         this.menu = Roo.menu.MenuMgr.get(this.menu);
11646     }
11647     // register listeners first!!  - so render can be captured..
11648     Roo.util.Observable.call(this);
11649     if(renderTo){
11650         this.render(renderTo);
11651     }
11652     
11653   
11654 };
11655
11656 Roo.extend(Roo.Button, Roo.util.Observable, {
11657     /**
11658      * 
11659      */
11660     
11661     /**
11662      * Read-only. True if this button is hidden
11663      * @type Boolean
11664      */
11665     hidden : false,
11666     /**
11667      * Read-only. True if this button is disabled
11668      * @type Boolean
11669      */
11670     disabled : false,
11671     /**
11672      * Read-only. True if this button is pressed (only if enableToggle = true)
11673      * @type Boolean
11674      */
11675     pressed : false,
11676
11677     /**
11678      * @cfg {Number} tabIndex 
11679      * The DOM tabIndex for this button (defaults to undefined)
11680      */
11681     tabIndex : undefined,
11682
11683     /**
11684      * @cfg {Boolean} enableToggle
11685      * True to enable pressed/not pressed toggling (defaults to false)
11686      */
11687     enableToggle: false,
11688     /**
11689      * @cfg {Mixed} menu
11690      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11691      */
11692     menu : undefined,
11693     /**
11694      * @cfg {String} menuAlign
11695      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11696      */
11697     menuAlign : "tl-bl?",
11698
11699     /**
11700      * @cfg {String} iconCls
11701      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11702      */
11703     iconCls : undefined,
11704     /**
11705      * @cfg {String} type
11706      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11707      */
11708     type : 'button',
11709
11710     // private
11711     menuClassTarget: 'tr',
11712
11713     /**
11714      * @cfg {String} clickEvent
11715      * The type of event to map to the button's event handler (defaults to 'click')
11716      */
11717     clickEvent : 'click',
11718
11719     /**
11720      * @cfg {Boolean} handleMouseEvents
11721      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11722      */
11723     handleMouseEvents : true,
11724
11725     /**
11726      * @cfg {String} tooltipType
11727      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11728      */
11729     tooltipType : 'qtip',
11730
11731     /**
11732      * @cfg {String} cls
11733      * A CSS class to apply to the button's main element.
11734      */
11735     
11736     /**
11737      * @cfg {Roo.Template} template (Optional)
11738      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11739      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11740      * require code modifications if required elements (e.g. a button) aren't present.
11741      */
11742
11743     // private
11744     render : function(renderTo){
11745         var btn;
11746         if(this.hideParent){
11747             this.parentEl = Roo.get(renderTo);
11748         }
11749         if(!this.dhconfig){
11750             if(!this.template){
11751                 if(!Roo.Button.buttonTemplate){
11752                     // hideous table template
11753                     Roo.Button.buttonTemplate = new Roo.Template(
11754                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11755                         '<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>',
11756                         "</tr></tbody></table>");
11757                 }
11758                 this.template = Roo.Button.buttonTemplate;
11759             }
11760             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11761             var btnEl = btn.child("button:first");
11762             btnEl.on('focus', this.onFocus, this);
11763             btnEl.on('blur', this.onBlur, this);
11764             if(this.cls){
11765                 btn.addClass(this.cls);
11766             }
11767             if(this.icon){
11768                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11769             }
11770             if(this.iconCls){
11771                 btnEl.addClass(this.iconCls);
11772                 if(!this.cls){
11773                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11774                 }
11775             }
11776             if(this.tabIndex !== undefined){
11777                 btnEl.dom.tabIndex = this.tabIndex;
11778             }
11779             if(this.tooltip){
11780                 if(typeof this.tooltip == 'object'){
11781                     Roo.QuickTips.tips(Roo.apply({
11782                           target: btnEl.id
11783                     }, this.tooltip));
11784                 } else {
11785                     btnEl.dom[this.tooltipType] = this.tooltip;
11786                 }
11787             }
11788         }else{
11789             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11790         }
11791         this.el = btn;
11792         if(this.id){
11793             this.el.dom.id = this.el.id = this.id;
11794         }
11795         if(this.menu){
11796             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11797             this.menu.on("show", this.onMenuShow, this);
11798             this.menu.on("hide", this.onMenuHide, this);
11799         }
11800         btn.addClass("x-btn");
11801         if(Roo.isIE && !Roo.isIE7){
11802             this.autoWidth.defer(1, this);
11803         }else{
11804             this.autoWidth();
11805         }
11806         if(this.handleMouseEvents){
11807             btn.on("mouseover", this.onMouseOver, this);
11808             btn.on("mouseout", this.onMouseOut, this);
11809             btn.on("mousedown", this.onMouseDown, this);
11810         }
11811         btn.on(this.clickEvent, this.onClick, this);
11812         //btn.on("mouseup", this.onMouseUp, this);
11813         if(this.hidden){
11814             this.hide();
11815         }
11816         if(this.disabled){
11817             this.disable();
11818         }
11819         Roo.ButtonToggleMgr.register(this);
11820         if(this.pressed){
11821             this.el.addClass("x-btn-pressed");
11822         }
11823         if(this.repeat){
11824             var repeater = new Roo.util.ClickRepeater(btn,
11825                 typeof this.repeat == "object" ? this.repeat : {}
11826             );
11827             repeater.on("click", this.onClick,  this);
11828         }
11829         
11830         this.fireEvent('render', this);
11831         
11832     },
11833     /**
11834      * Returns the button's underlying element
11835      * @return {Roo.Element} The element
11836      */
11837     getEl : function(){
11838         return this.el;  
11839     },
11840     
11841     /**
11842      * Destroys this Button and removes any listeners.
11843      */
11844     destroy : function(){
11845         Roo.ButtonToggleMgr.unregister(this);
11846         this.el.removeAllListeners();
11847         this.purgeListeners();
11848         this.el.remove();
11849     },
11850
11851     // private
11852     autoWidth : function(){
11853         if(this.el){
11854             this.el.setWidth("auto");
11855             if(Roo.isIE7 && Roo.isStrict){
11856                 var ib = this.el.child('button');
11857                 if(ib && ib.getWidth() > 20){
11858                     ib.clip();
11859                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11860                 }
11861             }
11862             if(this.minWidth){
11863                 if(this.hidden){
11864                     this.el.beginMeasure();
11865                 }
11866                 if(this.el.getWidth() < this.minWidth){
11867                     this.el.setWidth(this.minWidth);
11868                 }
11869                 if(this.hidden){
11870                     this.el.endMeasure();
11871                 }
11872             }
11873         }
11874     },
11875
11876     /**
11877      * Assigns this button's click handler
11878      * @param {Function} handler The function to call when the button is clicked
11879      * @param {Object} scope (optional) Scope for the function passed in
11880      */
11881     setHandler : function(handler, scope){
11882         this.handler = handler;
11883         this.scope = scope;  
11884     },
11885     
11886     /**
11887      * Sets this button's text
11888      * @param {String} text The button text
11889      */
11890     setText : function(text){
11891         this.text = text;
11892         if(this.el){
11893             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11894         }
11895         this.autoWidth();
11896     },
11897     
11898     /**
11899      * Gets the text for this button
11900      * @return {String} The button text
11901      */
11902     getText : function(){
11903         return this.text;  
11904     },
11905     
11906     /**
11907      * Show this button
11908      */
11909     show: function(){
11910         this.hidden = false;
11911         if(this.el){
11912             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11913         }
11914     },
11915     
11916     /**
11917      * Hide this button
11918      */
11919     hide: function(){
11920         this.hidden = true;
11921         if(this.el){
11922             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11923         }
11924     },
11925     
11926     /**
11927      * Convenience function for boolean show/hide
11928      * @param {Boolean} visible True to show, false to hide
11929      */
11930     setVisible: function(visible){
11931         if(visible) {
11932             this.show();
11933         }else{
11934             this.hide();
11935         }
11936     },
11937     
11938     /**
11939      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11940      * @param {Boolean} state (optional) Force a particular state
11941      */
11942     toggle : function(state){
11943         state = state === undefined ? !this.pressed : state;
11944         if(state != this.pressed){
11945             if(state){
11946                 this.el.addClass("x-btn-pressed");
11947                 this.pressed = true;
11948                 this.fireEvent("toggle", this, true);
11949             }else{
11950                 this.el.removeClass("x-btn-pressed");
11951                 this.pressed = false;
11952                 this.fireEvent("toggle", this, false);
11953             }
11954             if(this.toggleHandler){
11955                 this.toggleHandler.call(this.scope || this, this, state);
11956             }
11957         }
11958     },
11959     
11960     /**
11961      * Focus the button
11962      */
11963     focus : function(){
11964         this.el.child('button:first').focus();
11965     },
11966     
11967     /**
11968      * Disable this button
11969      */
11970     disable : function(){
11971         if(this.el){
11972             this.el.addClass("x-btn-disabled");
11973         }
11974         this.disabled = true;
11975     },
11976     
11977     /**
11978      * Enable this button
11979      */
11980     enable : function(){
11981         if(this.el){
11982             this.el.removeClass("x-btn-disabled");
11983         }
11984         this.disabled = false;
11985     },
11986
11987     /**
11988      * Convenience function for boolean enable/disable
11989      * @param {Boolean} enabled True to enable, false to disable
11990      */
11991     setDisabled : function(v){
11992         this[v !== true ? "enable" : "disable"]();
11993     },
11994
11995     // private
11996     onClick : function(e){
11997         if(e){
11998             e.preventDefault();
11999         }
12000         if(e.button != 0){
12001             return;
12002         }
12003         if(!this.disabled){
12004             if(this.enableToggle){
12005                 this.toggle();
12006             }
12007             if(this.menu && !this.menu.isVisible()){
12008                 this.menu.show(this.el, this.menuAlign);
12009             }
12010             this.fireEvent("click", this, e);
12011             if(this.handler){
12012                 this.el.removeClass("x-btn-over");
12013                 this.handler.call(this.scope || this, this, e);
12014             }
12015         }
12016     },
12017     // private
12018     onMouseOver : function(e){
12019         if(!this.disabled){
12020             this.el.addClass("x-btn-over");
12021             this.fireEvent('mouseover', this, e);
12022         }
12023     },
12024     // private
12025     onMouseOut : function(e){
12026         if(!e.within(this.el,  true)){
12027             this.el.removeClass("x-btn-over");
12028             this.fireEvent('mouseout', this, e);
12029         }
12030     },
12031     // private
12032     onFocus : function(e){
12033         if(!this.disabled){
12034             this.el.addClass("x-btn-focus");
12035         }
12036     },
12037     // private
12038     onBlur : function(e){
12039         this.el.removeClass("x-btn-focus");
12040     },
12041     // private
12042     onMouseDown : function(e){
12043         if(!this.disabled && e.button == 0){
12044             this.el.addClass("x-btn-click");
12045             Roo.get(document).on('mouseup', this.onMouseUp, this);
12046         }
12047     },
12048     // private
12049     onMouseUp : function(e){
12050         if(e.button == 0){
12051             this.el.removeClass("x-btn-click");
12052             Roo.get(document).un('mouseup', this.onMouseUp, this);
12053         }
12054     },
12055     // private
12056     onMenuShow : function(e){
12057         this.el.addClass("x-btn-menu-active");
12058     },
12059     // private
12060     onMenuHide : function(e){
12061         this.el.removeClass("x-btn-menu-active");
12062     }   
12063 });
12064
12065 // Private utility class used by Button
12066 Roo.ButtonToggleMgr = function(){
12067    var groups = {};
12068    
12069    function toggleGroup(btn, state){
12070        if(state){
12071            var g = groups[btn.toggleGroup];
12072            for(var i = 0, l = g.length; i < l; i++){
12073                if(g[i] != btn){
12074                    g[i].toggle(false);
12075                }
12076            }
12077        }
12078    }
12079    
12080    return {
12081        register : function(btn){
12082            if(!btn.toggleGroup){
12083                return;
12084            }
12085            var g = groups[btn.toggleGroup];
12086            if(!g){
12087                g = groups[btn.toggleGroup] = [];
12088            }
12089            g.push(btn);
12090            btn.on("toggle", toggleGroup);
12091        },
12092        
12093        unregister : function(btn){
12094            if(!btn.toggleGroup){
12095                return;
12096            }
12097            var g = groups[btn.toggleGroup];
12098            if(g){
12099                g.remove(btn);
12100                btn.un("toggle", toggleGroup);
12101            }
12102        }
12103    };
12104 }();/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114  
12115 /**
12116  * @class Roo.SplitButton
12117  * @extends Roo.Button
12118  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12119  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12120  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12121  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12122  * @cfg {String} arrowTooltip The title attribute of the arrow
12123  * @constructor
12124  * Create a new menu button
12125  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12126  * @param {Object} config The config object
12127  */
12128 Roo.SplitButton = function(renderTo, config){
12129     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12130     /**
12131      * @event arrowclick
12132      * Fires when this button's arrow is clicked
12133      * @param {SplitButton} this
12134      * @param {EventObject} e The click event
12135      */
12136     this.addEvents({"arrowclick":true});
12137 };
12138
12139 Roo.extend(Roo.SplitButton, Roo.Button, {
12140     render : function(renderTo){
12141         // this is one sweet looking template!
12142         var tpl = new Roo.Template(
12143             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12145             '<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>',
12146             "</tbody></table></td><td>",
12147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12148             '<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>',
12149             "</tbody></table></td></tr></table>"
12150         );
12151         var btn = tpl.append(renderTo, [this.text, this.type], true);
12152         var btnEl = btn.child("button");
12153         if(this.cls){
12154             btn.addClass(this.cls);
12155         }
12156         if(this.icon){
12157             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12158         }
12159         if(this.iconCls){
12160             btnEl.addClass(this.iconCls);
12161             if(!this.cls){
12162                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12163             }
12164         }
12165         this.el = btn;
12166         if(this.handleMouseEvents){
12167             btn.on("mouseover", this.onMouseOver, this);
12168             btn.on("mouseout", this.onMouseOut, this);
12169             btn.on("mousedown", this.onMouseDown, this);
12170             btn.on("mouseup", this.onMouseUp, this);
12171         }
12172         btn.on(this.clickEvent, this.onClick, this);
12173         if(this.tooltip){
12174             if(typeof this.tooltip == 'object'){
12175                 Roo.QuickTips.tips(Roo.apply({
12176                       target: btnEl.id
12177                 }, this.tooltip));
12178             } else {
12179                 btnEl.dom[this.tooltipType] = this.tooltip;
12180             }
12181         }
12182         if(this.arrowTooltip){
12183             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12184         }
12185         if(this.hidden){
12186             this.hide();
12187         }
12188         if(this.disabled){
12189             this.disable();
12190         }
12191         if(this.pressed){
12192             this.el.addClass("x-btn-pressed");
12193         }
12194         if(Roo.isIE && !Roo.isIE7){
12195             this.autoWidth.defer(1, this);
12196         }else{
12197             this.autoWidth();
12198         }
12199         if(this.menu){
12200             this.menu.on("show", this.onMenuShow, this);
12201             this.menu.on("hide", this.onMenuHide, this);
12202         }
12203         this.fireEvent('render', this);
12204     },
12205
12206     // private
12207     autoWidth : function(){
12208         if(this.el){
12209             var tbl = this.el.child("table:first");
12210             var tbl2 = this.el.child("table:last");
12211             this.el.setWidth("auto");
12212             tbl.setWidth("auto");
12213             if(Roo.isIE7 && Roo.isStrict){
12214                 var ib = this.el.child('button:first');
12215                 if(ib && ib.getWidth() > 20){
12216                     ib.clip();
12217                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12218                 }
12219             }
12220             if(this.minWidth){
12221                 if(this.hidden){
12222                     this.el.beginMeasure();
12223                 }
12224                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12225                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12226                 }
12227                 if(this.hidden){
12228                     this.el.endMeasure();
12229                 }
12230             }
12231             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12232         } 
12233     },
12234     /**
12235      * Sets this button's click handler
12236      * @param {Function} handler The function to call when the button is clicked
12237      * @param {Object} scope (optional) Scope for the function passed above
12238      */
12239     setHandler : function(handler, scope){
12240         this.handler = handler;
12241         this.scope = scope;  
12242     },
12243     
12244     /**
12245      * Sets this button's arrow click handler
12246      * @param {Function} handler The function to call when the arrow is clicked
12247      * @param {Object} scope (optional) Scope for the function passed above
12248      */
12249     setArrowHandler : function(handler, scope){
12250         this.arrowHandler = handler;
12251         this.scope = scope;  
12252     },
12253     
12254     /**
12255      * Focus the button
12256      */
12257     focus : function(){
12258         if(this.el){
12259             this.el.child("button:first").focus();
12260         }
12261     },
12262
12263     // private
12264     onClick : function(e){
12265         e.preventDefault();
12266         if(!this.disabled){
12267             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12268                 if(this.menu && !this.menu.isVisible()){
12269                     this.menu.show(this.el, this.menuAlign);
12270                 }
12271                 this.fireEvent("arrowclick", this, e);
12272                 if(this.arrowHandler){
12273                     this.arrowHandler.call(this.scope || this, this, e);
12274                 }
12275             }else{
12276                 this.fireEvent("click", this, e);
12277                 if(this.handler){
12278                     this.handler.call(this.scope || this, this, e);
12279                 }
12280             }
12281         }
12282     },
12283     // private
12284     onMouseDown : function(e){
12285         if(!this.disabled){
12286             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12287         }
12288     },
12289     // private
12290     onMouseUp : function(e){
12291         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12292     }   
12293 });
12294
12295
12296 // backwards compat
12297 Roo.MenuButton = Roo.SplitButton;/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.Toolbar
12310  * Basic Toolbar class.
12311  * @constructor
12312  * Creates a new Toolbar
12313  * @param {Object} container The config object
12314  */ 
12315 Roo.Toolbar = function(container, buttons, config)
12316 {
12317     /// old consturctor format still supported..
12318     if(container instanceof Array){ // omit the container for later rendering
12319         buttons = container;
12320         config = buttons;
12321         container = null;
12322     }
12323     if (typeof(container) == 'object' && container.xtype) {
12324         config = container;
12325         container = config.container;
12326         buttons = config.buttons || []; // not really - use items!!
12327     }
12328     var xitems = [];
12329     if (config && config.items) {
12330         xitems = config.items;
12331         delete config.items;
12332     }
12333     Roo.apply(this, config);
12334     this.buttons = buttons;
12335     
12336     if(container){
12337         this.render(container);
12338     }
12339     this.xitems = xitems;
12340     Roo.each(xitems, function(b) {
12341         this.add(b);
12342     }, this);
12343     
12344 };
12345
12346 Roo.Toolbar.prototype = {
12347     /**
12348      * @cfg {Array} items
12349      * array of button configs or elements to add (will be converted to a MixedCollection)
12350      */
12351     
12352     /**
12353      * @cfg {String/HTMLElement/Element} container
12354      * The id or element that will contain the toolbar
12355      */
12356     // private
12357     render : function(ct){
12358         this.el = Roo.get(ct);
12359         if(this.cls){
12360             this.el.addClass(this.cls);
12361         }
12362         // using a table allows for vertical alignment
12363         // 100% width is needed by Safari...
12364         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12365         this.tr = this.el.child("tr", true);
12366         var autoId = 0;
12367         this.items = new Roo.util.MixedCollection(false, function(o){
12368             return o.id || ("item" + (++autoId));
12369         });
12370         if(this.buttons){
12371             this.add.apply(this, this.buttons);
12372             delete this.buttons;
12373         }
12374     },
12375
12376     /**
12377      * Adds element(s) to the toolbar -- this function takes a variable number of 
12378      * arguments of mixed type and adds them to the toolbar.
12379      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12380      * <ul>
12381      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12382      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12383      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12384      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12385      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12386      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12387      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12388      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12389      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12390      * </ul>
12391      * @param {Mixed} arg2
12392      * @param {Mixed} etc.
12393      */
12394     add : function(){
12395         var a = arguments, l = a.length;
12396         for(var i = 0; i < l; i++){
12397             this._add(a[i]);
12398         }
12399     },
12400     // private..
12401     _add : function(el) {
12402         
12403         if (el.xtype) {
12404             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12405         }
12406         
12407         if (el.applyTo){ // some kind of form field
12408             return this.addField(el);
12409         } 
12410         if (el.render){ // some kind of Toolbar.Item
12411             return this.addItem(el);
12412         }
12413         if (typeof el == "string"){ // string
12414             if(el == "separator" || el == "-"){
12415                 return this.addSeparator();
12416             }
12417             if (el == " "){
12418                 return this.addSpacer();
12419             }
12420             if(el == "->"){
12421                 return this.addFill();
12422             }
12423             return this.addText(el);
12424             
12425         }
12426         if(el.tagName){ // element
12427             return this.addElement(el);
12428         }
12429         if(typeof el == "object"){ // must be button config?
12430             return this.addButton(el);
12431         }
12432         // and now what?!?!
12433         return false;
12434         
12435     },
12436     
12437     /**
12438      * Add an Xtype element
12439      * @param {Object} xtype Xtype Object
12440      * @return {Object} created Object
12441      */
12442     addxtype : function(e){
12443         return this.add(e);  
12444     },
12445     
12446     /**
12447      * Returns the Element for this toolbar.
12448      * @return {Roo.Element}
12449      */
12450     getEl : function(){
12451         return this.el;  
12452     },
12453     
12454     /**
12455      * Adds a separator
12456      * @return {Roo.Toolbar.Item} The separator item
12457      */
12458     addSeparator : function(){
12459         return this.addItem(new Roo.Toolbar.Separator());
12460     },
12461
12462     /**
12463      * Adds a spacer element
12464      * @return {Roo.Toolbar.Spacer} The spacer item
12465      */
12466     addSpacer : function(){
12467         return this.addItem(new Roo.Toolbar.Spacer());
12468     },
12469
12470     /**
12471      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12472      * @return {Roo.Toolbar.Fill} The fill item
12473      */
12474     addFill : function(){
12475         return this.addItem(new Roo.Toolbar.Fill());
12476     },
12477
12478     /**
12479      * Adds any standard HTML element to the toolbar
12480      * @param {String/HTMLElement/Element} el The element or id of the element to add
12481      * @return {Roo.Toolbar.Item} The element's item
12482      */
12483     addElement : function(el){
12484         return this.addItem(new Roo.Toolbar.Item(el));
12485     },
12486     /**
12487      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12488      * @type Roo.util.MixedCollection  
12489      */
12490     items : false,
12491      
12492     /**
12493      * Adds any Toolbar.Item or subclass
12494      * @param {Roo.Toolbar.Item} item
12495      * @return {Roo.Toolbar.Item} The item
12496      */
12497     addItem : function(item){
12498         var td = this.nextBlock();
12499         item.render(td);
12500         this.items.add(item);
12501         return item;
12502     },
12503     
12504     /**
12505      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12506      * @param {Object/Array} config A button config or array of configs
12507      * @return {Roo.Toolbar.Button/Array}
12508      */
12509     addButton : function(config){
12510         if(config instanceof Array){
12511             var buttons = [];
12512             for(var i = 0, len = config.length; i < len; i++) {
12513                 buttons.push(this.addButton(config[i]));
12514             }
12515             return buttons;
12516         }
12517         var b = config;
12518         if(!(config instanceof Roo.Toolbar.Button)){
12519             b = config.split ?
12520                 new Roo.Toolbar.SplitButton(config) :
12521                 new Roo.Toolbar.Button(config);
12522         }
12523         var td = this.nextBlock();
12524         b.render(td);
12525         this.items.add(b);
12526         return b;
12527     },
12528     
12529     /**
12530      * Adds text to the toolbar
12531      * @param {String} text The text to add
12532      * @return {Roo.Toolbar.Item} The element's item
12533      */
12534     addText : function(text){
12535         return this.addItem(new Roo.Toolbar.TextItem(text));
12536     },
12537     
12538     /**
12539      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12540      * @param {Number} index The index where the item is to be inserted
12541      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12542      * @return {Roo.Toolbar.Button/Item}
12543      */
12544     insertButton : function(index, item){
12545         if(item instanceof Array){
12546             var buttons = [];
12547             for(var i = 0, len = item.length; i < len; i++) {
12548                buttons.push(this.insertButton(index + i, item[i]));
12549             }
12550             return buttons;
12551         }
12552         if (!(item instanceof Roo.Toolbar.Button)){
12553            item = new Roo.Toolbar.Button(item);
12554         }
12555         var td = document.createElement("td");
12556         this.tr.insertBefore(td, this.tr.childNodes[index]);
12557         item.render(td);
12558         this.items.insert(index, item);
12559         return item;
12560     },
12561     
12562     /**
12563      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12564      * @param {Object} config
12565      * @return {Roo.Toolbar.Item} The element's item
12566      */
12567     addDom : function(config, returnEl){
12568         var td = this.nextBlock();
12569         Roo.DomHelper.overwrite(td, config);
12570         var ti = new Roo.Toolbar.Item(td.firstChild);
12571         ti.render(td);
12572         this.items.add(ti);
12573         return ti;
12574     },
12575
12576     /**
12577      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12578      * @type Roo.util.MixedCollection  
12579      */
12580     fields : false,
12581     
12582     /**
12583      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12584      * Note: the field should not have been rendered yet. For a field that has already been
12585      * rendered, use {@link #addElement}.
12586      * @param {Roo.form.Field} field
12587      * @return {Roo.ToolbarItem}
12588      */
12589      
12590       
12591     addField : function(field) {
12592         if (!this.fields) {
12593             var autoId = 0;
12594             this.fields = new Roo.util.MixedCollection(false, function(o){
12595                 return o.id || ("item" + (++autoId));
12596             });
12597
12598         }
12599         
12600         var td = this.nextBlock();
12601         field.render(td);
12602         var ti = new Roo.Toolbar.Item(td.firstChild);
12603         ti.render(td);
12604         this.items.add(ti);
12605         this.fields.add(field);
12606         return ti;
12607     },
12608     /**
12609      * Hide the toolbar
12610      * @method hide
12611      */
12612      
12613       
12614     hide : function()
12615     {
12616         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12617         this.el.child('div').hide();
12618     },
12619     /**
12620      * Show the toolbar
12621      * @method show
12622      */
12623     show : function()
12624     {
12625         this.el.child('div').show();
12626     },
12627       
12628     // private
12629     nextBlock : function(){
12630         var td = document.createElement("td");
12631         this.tr.appendChild(td);
12632         return td;
12633     },
12634
12635     // private
12636     destroy : function(){
12637         if(this.items){ // rendered?
12638             Roo.destroy.apply(Roo, this.items.items);
12639         }
12640         if(this.fields){ // rendered?
12641             Roo.destroy.apply(Roo, this.fields.items);
12642         }
12643         Roo.Element.uncache(this.el, this.tr);
12644     }
12645 };
12646
12647 /**
12648  * @class Roo.Toolbar.Item
12649  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12650  * @constructor
12651  * Creates a new Item
12652  * @param {HTMLElement} el 
12653  */
12654 Roo.Toolbar.Item = function(el){
12655     this.el = Roo.getDom(el);
12656     this.id = Roo.id(this.el);
12657     this.hidden = false;
12658 };
12659
12660 Roo.Toolbar.Item.prototype = {
12661     
12662     /**
12663      * Get this item's HTML Element
12664      * @return {HTMLElement}
12665      */
12666     getEl : function(){
12667        return this.el;  
12668     },
12669
12670     // private
12671     render : function(td){
12672         this.td = td;
12673         td.appendChild(this.el);
12674     },
12675     
12676     /**
12677      * Removes and destroys this item.
12678      */
12679     destroy : function(){
12680         this.td.parentNode.removeChild(this.td);
12681     },
12682     
12683     /**
12684      * Shows this item.
12685      */
12686     show: function(){
12687         this.hidden = false;
12688         this.td.style.display = "";
12689     },
12690     
12691     /**
12692      * Hides this item.
12693      */
12694     hide: function(){
12695         this.hidden = true;
12696         this.td.style.display = "none";
12697     },
12698     
12699     /**
12700      * Convenience function for boolean show/hide.
12701      * @param {Boolean} visible true to show/false to hide
12702      */
12703     setVisible: function(visible){
12704         if(visible) {
12705             this.show();
12706         }else{
12707             this.hide();
12708         }
12709     },
12710     
12711     /**
12712      * Try to focus this item.
12713      */
12714     focus : function(){
12715         Roo.fly(this.el).focus();
12716     },
12717     
12718     /**
12719      * Disables this item.
12720      */
12721     disable : function(){
12722         Roo.fly(this.td).addClass("x-item-disabled");
12723         this.disabled = true;
12724         this.el.disabled = true;
12725     },
12726     
12727     /**
12728      * Enables this item.
12729      */
12730     enable : function(){
12731         Roo.fly(this.td).removeClass("x-item-disabled");
12732         this.disabled = false;
12733         this.el.disabled = false;
12734     }
12735 };
12736
12737
12738 /**
12739  * @class Roo.Toolbar.Separator
12740  * @extends Roo.Toolbar.Item
12741  * A simple toolbar separator class
12742  * @constructor
12743  * Creates a new Separator
12744  */
12745 Roo.Toolbar.Separator = function(){
12746     var s = document.createElement("span");
12747     s.className = "ytb-sep";
12748     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12749 };
12750 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12751     enable:Roo.emptyFn,
12752     disable:Roo.emptyFn,
12753     focus:Roo.emptyFn
12754 });
12755
12756 /**
12757  * @class Roo.Toolbar.Spacer
12758  * @extends Roo.Toolbar.Item
12759  * A simple element that adds extra horizontal space to a toolbar.
12760  * @constructor
12761  * Creates a new Spacer
12762  */
12763 Roo.Toolbar.Spacer = function(){
12764     var s = document.createElement("div");
12765     s.className = "ytb-spacer";
12766     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12767 };
12768 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12769     enable:Roo.emptyFn,
12770     disable:Roo.emptyFn,
12771     focus:Roo.emptyFn
12772 });
12773
12774 /**
12775  * @class Roo.Toolbar.Fill
12776  * @extends Roo.Toolbar.Spacer
12777  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12778  * @constructor
12779  * Creates a new Spacer
12780  */
12781 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12782     // private
12783     render : function(td){
12784         td.style.width = '100%';
12785         Roo.Toolbar.Fill.superclass.render.call(this, td);
12786     }
12787 });
12788
12789 /**
12790  * @class Roo.Toolbar.TextItem
12791  * @extends Roo.Toolbar.Item
12792  * A simple class that renders text directly into a toolbar.
12793  * @constructor
12794  * Creates a new TextItem
12795  * @param {String} text
12796  */
12797 Roo.Toolbar.TextItem = function(text){
12798     if (typeof(text) == 'object') {
12799         text = text.text;
12800     }
12801     var s = document.createElement("span");
12802     s.className = "ytb-text";
12803     s.innerHTML = text;
12804     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12805 };
12806 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12807     enable:Roo.emptyFn,
12808     disable:Roo.emptyFn,
12809     focus:Roo.emptyFn
12810 });
12811
12812 /**
12813  * @class Roo.Toolbar.Button
12814  * @extends Roo.Button
12815  * A button that renders into a toolbar.
12816  * @constructor
12817  * Creates a new Button
12818  * @param {Object} config A standard {@link Roo.Button} config object
12819  */
12820 Roo.Toolbar.Button = function(config){
12821     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12822 };
12823 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12824     render : function(td){
12825         this.td = td;
12826         Roo.Toolbar.Button.superclass.render.call(this, td);
12827     },
12828     
12829     /**
12830      * Removes and destroys this button
12831      */
12832     destroy : function(){
12833         Roo.Toolbar.Button.superclass.destroy.call(this);
12834         this.td.parentNode.removeChild(this.td);
12835     },
12836     
12837     /**
12838      * Shows this button
12839      */
12840     show: function(){
12841         this.hidden = false;
12842         this.td.style.display = "";
12843     },
12844     
12845     /**
12846      * Hides this button
12847      */
12848     hide: function(){
12849         this.hidden = true;
12850         this.td.style.display = "none";
12851     },
12852
12853     /**
12854      * Disables this item
12855      */
12856     disable : function(){
12857         Roo.fly(this.td).addClass("x-item-disabled");
12858         this.disabled = true;
12859     },
12860
12861     /**
12862      * Enables this item
12863      */
12864     enable : function(){
12865         Roo.fly(this.td).removeClass("x-item-disabled");
12866         this.disabled = false;
12867     }
12868 });
12869 // backwards compat
12870 Roo.ToolbarButton = Roo.Toolbar.Button;
12871
12872 /**
12873  * @class Roo.Toolbar.SplitButton
12874  * @extends Roo.SplitButton
12875  * A menu button that renders into a toolbar.
12876  * @constructor
12877  * Creates a new SplitButton
12878  * @param {Object} config A standard {@link Roo.SplitButton} config object
12879  */
12880 Roo.Toolbar.SplitButton = function(config){
12881     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12882 };
12883 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12884     render : function(td){
12885         this.td = td;
12886         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12887     },
12888     
12889     /**
12890      * Removes and destroys this button
12891      */
12892     destroy : function(){
12893         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12894         this.td.parentNode.removeChild(this.td);
12895     },
12896     
12897     /**
12898      * Shows this button
12899      */
12900     show: function(){
12901         this.hidden = false;
12902         this.td.style.display = "";
12903     },
12904     
12905     /**
12906      * Hides this button
12907      */
12908     hide: function(){
12909         this.hidden = true;
12910         this.td.style.display = "none";
12911     }
12912 });
12913
12914 // backwards compat
12915 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12916  * Based on:
12917  * Ext JS Library 1.1.1
12918  * Copyright(c) 2006-2007, Ext JS, LLC.
12919  *
12920  * Originally Released Under LGPL - original licence link has changed is not relivant.
12921  *
12922  * Fork - LGPL
12923  * <script type="text/javascript">
12924  */
12925  
12926 /**
12927  * @class Roo.PagingToolbar
12928  * @extends Roo.Toolbar
12929  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12930  * @constructor
12931  * Create a new PagingToolbar
12932  * @param {Object} config The config object
12933  */
12934 Roo.PagingToolbar = function(el, ds, config)
12935 {
12936     // old args format still supported... - xtype is prefered..
12937     if (typeof(el) == 'object' && el.xtype) {
12938         // created from xtype...
12939         config = el;
12940         ds = el.dataSource;
12941         el = config.container;
12942     }
12943     var items = [];
12944     if (config.items) {
12945         items = config.items;
12946         config.items = [];
12947     }
12948     
12949     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12950     this.ds = ds;
12951     this.cursor = 0;
12952     this.renderButtons(this.el);
12953     this.bind(ds);
12954     
12955     // supprot items array.
12956    
12957     Roo.each(items, function(e) {
12958         this.add(Roo.factory(e));
12959     },this);
12960     
12961 };
12962
12963 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12964     /**
12965      * @cfg {Roo.data.Store} dataSource
12966      * The underlying data store providing the paged data
12967      */
12968     /**
12969      * @cfg {String/HTMLElement/Element} container
12970      * container The id or element that will contain the toolbar
12971      */
12972     /**
12973      * @cfg {Boolean} displayInfo
12974      * True to display the displayMsg (defaults to false)
12975      */
12976     /**
12977      * @cfg {Number} pageSize
12978      * The number of records to display per page (defaults to 20)
12979      */
12980     pageSize: 20,
12981     /**
12982      * @cfg {String} displayMsg
12983      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12984      */
12985     displayMsg : 'Displaying {0} - {1} of {2}',
12986     /**
12987      * @cfg {String} emptyMsg
12988      * The message to display when no records are found (defaults to "No data to display")
12989      */
12990     emptyMsg : 'No data to display',
12991     /**
12992      * Customizable piece of the default paging text (defaults to "Page")
12993      * @type String
12994      */
12995     beforePageText : "Page",
12996     /**
12997      * Customizable piece of the default paging text (defaults to "of %0")
12998      * @type String
12999      */
13000     afterPageText : "of {0}",
13001     /**
13002      * Customizable piece of the default paging text (defaults to "First Page")
13003      * @type String
13004      */
13005     firstText : "First Page",
13006     /**
13007      * Customizable piece of the default paging text (defaults to "Previous Page")
13008      * @type String
13009      */
13010     prevText : "Previous Page",
13011     /**
13012      * Customizable piece of the default paging text (defaults to "Next Page")
13013      * @type String
13014      */
13015     nextText : "Next Page",
13016     /**
13017      * Customizable piece of the default paging text (defaults to "Last Page")
13018      * @type String
13019      */
13020     lastText : "Last Page",
13021     /**
13022      * Customizable piece of the default paging text (defaults to "Refresh")
13023      * @type String
13024      */
13025     refreshText : "Refresh",
13026
13027     // private
13028     renderButtons : function(el){
13029         Roo.PagingToolbar.superclass.render.call(this, el);
13030         this.first = this.addButton({
13031             tooltip: this.firstText,
13032             cls: "x-btn-icon x-grid-page-first",
13033             disabled: true,
13034             handler: this.onClick.createDelegate(this, ["first"])
13035         });
13036         this.prev = this.addButton({
13037             tooltip: this.prevText,
13038             cls: "x-btn-icon x-grid-page-prev",
13039             disabled: true,
13040             handler: this.onClick.createDelegate(this, ["prev"])
13041         });
13042         //this.addSeparator();
13043         this.add(this.beforePageText);
13044         this.field = Roo.get(this.addDom({
13045            tag: "input",
13046            type: "text",
13047            size: "3",
13048            value: "1",
13049            cls: "x-grid-page-number"
13050         }).el);
13051         this.field.on("keydown", this.onPagingKeydown, this);
13052         this.field.on("focus", function(){this.dom.select();});
13053         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13054         this.field.setHeight(18);
13055         //this.addSeparator();
13056         this.next = this.addButton({
13057             tooltip: this.nextText,
13058             cls: "x-btn-icon x-grid-page-next",
13059             disabled: true,
13060             handler: this.onClick.createDelegate(this, ["next"])
13061         });
13062         this.last = this.addButton({
13063             tooltip: this.lastText,
13064             cls: "x-btn-icon x-grid-page-last",
13065             disabled: true,
13066             handler: this.onClick.createDelegate(this, ["last"])
13067         });
13068         //this.addSeparator();
13069         this.loading = this.addButton({
13070             tooltip: this.refreshText,
13071             cls: "x-btn-icon x-grid-loading",
13072             handler: this.onClick.createDelegate(this, ["refresh"])
13073         });
13074
13075         if(this.displayInfo){
13076             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13077         }
13078     },
13079
13080     // private
13081     updateInfo : function(){
13082         if(this.displayEl){
13083             var count = this.ds.getCount();
13084             var msg = count == 0 ?
13085                 this.emptyMsg :
13086                 String.format(
13087                     this.displayMsg,
13088                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13089                 );
13090             this.displayEl.update(msg);
13091         }
13092     },
13093
13094     // private
13095     onLoad : function(ds, r, o){
13096        this.cursor = o.params ? o.params.start : 0;
13097        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13098
13099        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13100        this.field.dom.value = ap;
13101        this.first.setDisabled(ap == 1);
13102        this.prev.setDisabled(ap == 1);
13103        this.next.setDisabled(ap == ps);
13104        this.last.setDisabled(ap == ps);
13105        this.loading.enable();
13106        this.updateInfo();
13107     },
13108
13109     // private
13110     getPageData : function(){
13111         var total = this.ds.getTotalCount();
13112         return {
13113             total : total,
13114             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13115             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13116         };
13117     },
13118
13119     // private
13120     onLoadError : function(){
13121         this.loading.enable();
13122     },
13123
13124     // private
13125     onPagingKeydown : function(e){
13126         var k = e.getKey();
13127         var d = this.getPageData();
13128         if(k == e.RETURN){
13129             var v = this.field.dom.value, pageNum;
13130             if(!v || isNaN(pageNum = parseInt(v, 10))){
13131                 this.field.dom.value = d.activePage;
13132                 return;
13133             }
13134             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13135             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13136             e.stopEvent();
13137         }
13138         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))
13139         {
13140           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13141           this.field.dom.value = pageNum;
13142           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13143           e.stopEvent();
13144         }
13145         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13146         {
13147           var v = this.field.dom.value, pageNum; 
13148           var increment = (e.shiftKey) ? 10 : 1;
13149           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13150             increment *= -1;
13151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13152             this.field.dom.value = d.activePage;
13153             return;
13154           }
13155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13156           {
13157             this.field.dom.value = parseInt(v, 10) + increment;
13158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13160           }
13161           e.stopEvent();
13162         }
13163     },
13164
13165     // private
13166     beforeLoad : function(){
13167         if(this.loading){
13168             this.loading.disable();
13169         }
13170     },
13171
13172     // private
13173     onClick : function(which){
13174         var ds = this.ds;
13175         switch(which){
13176             case "first":
13177                 ds.load({params:{start: 0, limit: this.pageSize}});
13178             break;
13179             case "prev":
13180                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13181             break;
13182             case "next":
13183                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13184             break;
13185             case "last":
13186                 var total = ds.getTotalCount();
13187                 var extra = total % this.pageSize;
13188                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13189                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13190             break;
13191             case "refresh":
13192                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13193             break;
13194         }
13195     },
13196
13197     /**
13198      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13199      * @param {Roo.data.Store} store The data store to unbind
13200      */
13201     unbind : function(ds){
13202         ds.un("beforeload", this.beforeLoad, this);
13203         ds.un("load", this.onLoad, this);
13204         ds.un("loadexception", this.onLoadError, this);
13205         ds.un("remove", this.updateInfo, this);
13206         ds.un("add", this.updateInfo, this);
13207         this.ds = undefined;
13208     },
13209
13210     /**
13211      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13212      * @param {Roo.data.Store} store The data store to bind
13213      */
13214     bind : function(ds){
13215         ds.on("beforeload", this.beforeLoad, this);
13216         ds.on("load", this.onLoad, this);
13217         ds.on("loadexception", this.onLoadError, this);
13218         ds.on("remove", this.updateInfo, this);
13219         ds.on("add", this.updateInfo, this);
13220         this.ds = ds;
13221     }
13222 });/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234  * @class Roo.Resizable
13235  * @extends Roo.util.Observable
13236  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13237  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13238  * 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
13239  * the element will be wrapped for you automatically.</p>
13240  * <p>Here is the list of valid resize handles:</p>
13241  * <pre>
13242 Value   Description
13243 ------  -------------------
13244  'n'     north
13245  's'     south
13246  'e'     east
13247  'w'     west
13248  'nw'    northwest
13249  'sw'    southwest
13250  'se'    southeast
13251  'ne'    northeast
13252  'hd'    horizontal drag
13253  'all'   all
13254 </pre>
13255  * <p>Here's an example showing the creation of a typical Resizable:</p>
13256  * <pre><code>
13257 var resizer = new Roo.Resizable("element-id", {
13258     handles: 'all',
13259     minWidth: 200,
13260     minHeight: 100,
13261     maxWidth: 500,
13262     maxHeight: 400,
13263     pinned: true
13264 });
13265 resizer.on("resize", myHandler);
13266 </code></pre>
13267  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13268  * resizer.east.setDisplayed(false);</p>
13269  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13270  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13271  * resize operation's new size (defaults to [0, 0])
13272  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13273  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13274  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13275  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13276  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13277  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13278  * @cfg {Number} width The width of the element in pixels (defaults to null)
13279  * @cfg {Number} height The height of the element in pixels (defaults to null)
13280  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13281  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13282  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13283  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13284  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13285  * in favor of the handles config option (defaults to false)
13286  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13287  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13288  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13289  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13290  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13291  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13292  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13293  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13294  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13295  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13296  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13297  * @constructor
13298  * Create a new resizable component
13299  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13300  * @param {Object} config configuration options
13301   */
13302 Roo.Resizable = function(el, config)
13303 {
13304     this.el = Roo.get(el);
13305
13306     if(config && config.wrap){
13307         config.resizeChild = this.el;
13308         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13309         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13310         this.el.setStyle("overflow", "hidden");
13311         this.el.setPositioning(config.resizeChild.getPositioning());
13312         config.resizeChild.clearPositioning();
13313         if(!config.width || !config.height){
13314             var csize = config.resizeChild.getSize();
13315             this.el.setSize(csize.width, csize.height);
13316         }
13317         if(config.pinned && !config.adjustments){
13318             config.adjustments = "auto";
13319         }
13320     }
13321
13322     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13323     this.proxy.unselectable();
13324     this.proxy.enableDisplayMode('block');
13325
13326     Roo.apply(this, config);
13327
13328     if(this.pinned){
13329         this.disableTrackOver = true;
13330         this.el.addClass("x-resizable-pinned");
13331     }
13332     // if the element isn't positioned, make it relative
13333     var position = this.el.getStyle("position");
13334     if(position != "absolute" && position != "fixed"){
13335         this.el.setStyle("position", "relative");
13336     }
13337     if(!this.handles){ // no handles passed, must be legacy style
13338         this.handles = 's,e,se';
13339         if(this.multiDirectional){
13340             this.handles += ',n,w';
13341         }
13342     }
13343     if(this.handles == "all"){
13344         this.handles = "n s e w ne nw se sw";
13345     }
13346     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13347     var ps = Roo.Resizable.positions;
13348     for(var i = 0, len = hs.length; i < len; i++){
13349         if(hs[i] && ps[hs[i]]){
13350             var pos = ps[hs[i]];
13351             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13352         }
13353     }
13354     // legacy
13355     this.corner = this.southeast;
13356     
13357     // updateBox = the box can move..
13358     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13359         this.updateBox = true;
13360     }
13361
13362     this.activeHandle = null;
13363
13364     if(this.resizeChild){
13365         if(typeof this.resizeChild == "boolean"){
13366             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13367         }else{
13368             this.resizeChild = Roo.get(this.resizeChild, true);
13369         }
13370     }
13371     
13372     if(this.adjustments == "auto"){
13373         var rc = this.resizeChild;
13374         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13375         if(rc && (hw || hn)){
13376             rc.position("relative");
13377             rc.setLeft(hw ? hw.el.getWidth() : 0);
13378             rc.setTop(hn ? hn.el.getHeight() : 0);
13379         }
13380         this.adjustments = [
13381             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13382             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13383         ];
13384     }
13385
13386     if(this.draggable){
13387         this.dd = this.dynamic ?
13388             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13389         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13390     }
13391
13392     // public events
13393     this.addEvents({
13394         /**
13395          * @event beforeresize
13396          * Fired before resize is allowed. Set enabled to false to cancel resize.
13397          * @param {Roo.Resizable} this
13398          * @param {Roo.EventObject} e The mousedown event
13399          */
13400         "beforeresize" : true,
13401         /**
13402          * @event resize
13403          * Fired after a resize.
13404          * @param {Roo.Resizable} this
13405          * @param {Number} width The new width
13406          * @param {Number} height The new height
13407          * @param {Roo.EventObject} e The mouseup event
13408          */
13409         "resize" : true
13410     });
13411
13412     if(this.width !== null && this.height !== null){
13413         this.resizeTo(this.width, this.height);
13414     }else{
13415         this.updateChildSize();
13416     }
13417     if(Roo.isIE){
13418         this.el.dom.style.zoom = 1;
13419     }
13420     Roo.Resizable.superclass.constructor.call(this);
13421 };
13422
13423 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13424         resizeChild : false,
13425         adjustments : [0, 0],
13426         minWidth : 5,
13427         minHeight : 5,
13428         maxWidth : 10000,
13429         maxHeight : 10000,
13430         enabled : true,
13431         animate : false,
13432         duration : .35,
13433         dynamic : false,
13434         handles : false,
13435         multiDirectional : false,
13436         disableTrackOver : false,
13437         easing : 'easeOutStrong',
13438         widthIncrement : 0,
13439         heightIncrement : 0,
13440         pinned : false,
13441         width : null,
13442         height : null,
13443         preserveRatio : false,
13444         transparent: false,
13445         minX: 0,
13446         minY: 0,
13447         draggable: false,
13448
13449         /**
13450          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13451          */
13452         constrainTo: undefined,
13453         /**
13454          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13455          */
13456         resizeRegion: undefined,
13457
13458
13459     /**
13460      * Perform a manual resize
13461      * @param {Number} width
13462      * @param {Number} height
13463      */
13464     resizeTo : function(width, height){
13465         this.el.setSize(width, height);
13466         this.updateChildSize();
13467         this.fireEvent("resize", this, width, height, null);
13468     },
13469
13470     // private
13471     startSizing : function(e, handle){
13472         this.fireEvent("beforeresize", this, e);
13473         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13474
13475             if(!this.overlay){
13476                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13477                 this.overlay.unselectable();
13478                 this.overlay.enableDisplayMode("block");
13479                 this.overlay.on("mousemove", this.onMouseMove, this);
13480                 this.overlay.on("mouseup", this.onMouseUp, this);
13481             }
13482             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13483
13484             this.resizing = true;
13485             this.startBox = this.el.getBox();
13486             this.startPoint = e.getXY();
13487             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13488                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13489
13490             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13491             this.overlay.show();
13492
13493             if(this.constrainTo) {
13494                 var ct = Roo.get(this.constrainTo);
13495                 this.resizeRegion = ct.getRegion().adjust(
13496                     ct.getFrameWidth('t'),
13497                     ct.getFrameWidth('l'),
13498                     -ct.getFrameWidth('b'),
13499                     -ct.getFrameWidth('r')
13500                 );
13501             }
13502
13503             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13504             this.proxy.show();
13505             this.proxy.setBox(this.startBox);
13506             if(!this.dynamic){
13507                 this.proxy.setStyle('visibility', 'visible');
13508             }
13509         }
13510     },
13511
13512     // private
13513     onMouseDown : function(handle, e){
13514         if(this.enabled){
13515             e.stopEvent();
13516             this.activeHandle = handle;
13517             this.startSizing(e, handle);
13518         }
13519     },
13520
13521     // private
13522     onMouseUp : function(e){
13523         var size = this.resizeElement();
13524         this.resizing = false;
13525         this.handleOut();
13526         this.overlay.hide();
13527         this.proxy.hide();
13528         this.fireEvent("resize", this, size.width, size.height, e);
13529     },
13530
13531     // private
13532     updateChildSize : function(){
13533         if(this.resizeChild){
13534             var el = this.el;
13535             var child = this.resizeChild;
13536             var adj = this.adjustments;
13537             if(el.dom.offsetWidth){
13538                 var b = el.getSize(true);
13539                 child.setSize(b.width+adj[0], b.height+adj[1]);
13540             }
13541             // Second call here for IE
13542             // The first call enables instant resizing and
13543             // the second call corrects scroll bars if they
13544             // exist
13545             if(Roo.isIE){
13546                 setTimeout(function(){
13547                     if(el.dom.offsetWidth){
13548                         var b = el.getSize(true);
13549                         child.setSize(b.width+adj[0], b.height+adj[1]);
13550                     }
13551                 }, 10);
13552             }
13553         }
13554     },
13555
13556     // private
13557     snap : function(value, inc, min){
13558         if(!inc || !value) return value;
13559         var newValue = value;
13560         var m = value % inc;
13561         if(m > 0){
13562             if(m > (inc/2)){
13563                 newValue = value + (inc-m);
13564             }else{
13565                 newValue = value - m;
13566             }
13567         }
13568         return Math.max(min, newValue);
13569     },
13570
13571     // private
13572     resizeElement : function(){
13573         var box = this.proxy.getBox();
13574         if(this.updateBox){
13575             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13576         }else{
13577             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13578         }
13579         this.updateChildSize();
13580         if(!this.dynamic){
13581             this.proxy.hide();
13582         }
13583         return box;
13584     },
13585
13586     // private
13587     constrain : function(v, diff, m, mx){
13588         if(v - diff < m){
13589             diff = v - m;
13590         }else if(v - diff > mx){
13591             diff = mx - v;
13592         }
13593         return diff;
13594     },
13595
13596     // private
13597     onMouseMove : function(e){
13598         if(this.enabled){
13599             try{// try catch so if something goes wrong the user doesn't get hung
13600
13601             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13602                 return;
13603             }
13604
13605             //var curXY = this.startPoint;
13606             var curSize = this.curSize || this.startBox;
13607             var x = this.startBox.x, y = this.startBox.y;
13608             var ox = x, oy = y;
13609             var w = curSize.width, h = curSize.height;
13610             var ow = w, oh = h;
13611             var mw = this.minWidth, mh = this.minHeight;
13612             var mxw = this.maxWidth, mxh = this.maxHeight;
13613             var wi = this.widthIncrement;
13614             var hi = this.heightIncrement;
13615
13616             var eventXY = e.getXY();
13617             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13618             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13619
13620             var pos = this.activeHandle.position;
13621
13622             switch(pos){
13623                 case "east":
13624                     w += diffX;
13625                     w = Math.min(Math.max(mw, w), mxw);
13626                     break;
13627              
13628                 case "south":
13629                     h += diffY;
13630                     h = Math.min(Math.max(mh, h), mxh);
13631                     break;
13632                 case "southeast":
13633                     w += diffX;
13634                     h += diffY;
13635                     w = Math.min(Math.max(mw, w), mxw);
13636                     h = Math.min(Math.max(mh, h), mxh);
13637                     break;
13638                 case "north":
13639                     diffY = this.constrain(h, diffY, mh, mxh);
13640                     y += diffY;
13641                     h -= diffY;
13642                     break;
13643                 case "hdrag":
13644                     
13645                     if (wi) {
13646                         var adiffX = Math.abs(diffX);
13647                         var sub = (adiffX % wi); // how much 
13648                         if (sub > (wi/2)) { // far enough to snap
13649                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13650                         } else {
13651                             // remove difference.. 
13652                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13653                         }
13654                     }
13655                     x += diffX;
13656                     x = Math.max(this.minX, x);
13657                     break;
13658                 case "west":
13659                     diffX = this.constrain(w, diffX, mw, mxw);
13660                     x += diffX;
13661                     w -= diffX;
13662                     break;
13663                 case "northeast":
13664                     w += diffX;
13665                     w = Math.min(Math.max(mw, w), mxw);
13666                     diffY = this.constrain(h, diffY, mh, mxh);
13667                     y += diffY;
13668                     h -= diffY;
13669                     break;
13670                 case "northwest":
13671                     diffX = this.constrain(w, diffX, mw, mxw);
13672                     diffY = this.constrain(h, diffY, mh, mxh);
13673                     y += diffY;
13674                     h -= diffY;
13675                     x += diffX;
13676                     w -= diffX;
13677                     break;
13678                case "southwest":
13679                     diffX = this.constrain(w, diffX, mw, mxw);
13680                     h += diffY;
13681                     h = Math.min(Math.max(mh, h), mxh);
13682                     x += diffX;
13683                     w -= diffX;
13684                     break;
13685             }
13686
13687             var sw = this.snap(w, wi, mw);
13688             var sh = this.snap(h, hi, mh);
13689             if(sw != w || sh != h){
13690                 switch(pos){
13691                     case "northeast":
13692                         y -= sh - h;
13693                     break;
13694                     case "north":
13695                         y -= sh - h;
13696                         break;
13697                     case "southwest":
13698                         x -= sw - w;
13699                     break;
13700                     case "west":
13701                         x -= sw - w;
13702                         break;
13703                     case "northwest":
13704                         x -= sw - w;
13705                         y -= sh - h;
13706                     break;
13707                 }
13708                 w = sw;
13709                 h = sh;
13710             }
13711
13712             if(this.preserveRatio){
13713                 switch(pos){
13714                     case "southeast":
13715                     case "east":
13716                         h = oh * (w/ow);
13717                         h = Math.min(Math.max(mh, h), mxh);
13718                         w = ow * (h/oh);
13719                        break;
13720                     case "south":
13721                         w = ow * (h/oh);
13722                         w = Math.min(Math.max(mw, w), mxw);
13723                         h = oh * (w/ow);
13724                         break;
13725                     case "northeast":
13726                         w = ow * (h/oh);
13727                         w = Math.min(Math.max(mw, w), mxw);
13728                         h = oh * (w/ow);
13729                     break;
13730                     case "north":
13731                         var tw = w;
13732                         w = ow * (h/oh);
13733                         w = Math.min(Math.max(mw, w), mxw);
13734                         h = oh * (w/ow);
13735                         x += (tw - w) / 2;
13736                         break;
13737                     case "southwest":
13738                         h = oh * (w/ow);
13739                         h = Math.min(Math.max(mh, h), mxh);
13740                         var tw = w;
13741                         w = ow * (h/oh);
13742                         x += tw - w;
13743                         break;
13744                     case "west":
13745                         var th = h;
13746                         h = oh * (w/ow);
13747                         h = Math.min(Math.max(mh, h), mxh);
13748                         y += (th - h) / 2;
13749                         var tw = w;
13750                         w = ow * (h/oh);
13751                         x += tw - w;
13752                        break;
13753                     case "northwest":
13754                         var tw = w;
13755                         var th = h;
13756                         h = oh * (w/ow);
13757                         h = Math.min(Math.max(mh, h), mxh);
13758                         w = ow * (h/oh);
13759                         y += th - h;
13760                         x += tw - w;
13761                        break;
13762
13763                 }
13764             }
13765             if (pos == 'hdrag') {
13766                 w = ow;
13767             }
13768             this.proxy.setBounds(x, y, w, h);
13769             if(this.dynamic){
13770                 this.resizeElement();
13771             }
13772             }catch(e){}
13773         }
13774     },
13775
13776     // private
13777     handleOver : function(){
13778         if(this.enabled){
13779             this.el.addClass("x-resizable-over");
13780         }
13781     },
13782
13783     // private
13784     handleOut : function(){
13785         if(!this.resizing){
13786             this.el.removeClass("x-resizable-over");
13787         }
13788     },
13789
13790     /**
13791      * Returns the element this component is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.el;
13796     },
13797
13798     /**
13799      * Returns the resizeChild element (or null).
13800      * @return {Roo.Element}
13801      */
13802     getResizeChild : function(){
13803         return this.resizeChild;
13804     },
13805
13806     /**
13807      * Destroys this resizable. If the element was wrapped and
13808      * removeEl is not true then the element remains.
13809      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13810      */
13811     destroy : function(removeEl){
13812         this.proxy.remove();
13813         if(this.overlay){
13814             this.overlay.removeAllListeners();
13815             this.overlay.remove();
13816         }
13817         var ps = Roo.Resizable.positions;
13818         for(var k in ps){
13819             if(typeof ps[k] != "function" && this[ps[k]]){
13820                 var h = this[ps[k]];
13821                 h.el.removeAllListeners();
13822                 h.el.remove();
13823             }
13824         }
13825         if(removeEl){
13826             this.el.update("");
13827             this.el.remove();
13828         }
13829     }
13830 });
13831
13832 // private
13833 // hash to map config positions to true positions
13834 Roo.Resizable.positions = {
13835     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13836     hd: "hdrag"
13837 };
13838
13839 // private
13840 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13841     if(!this.tpl){
13842         // only initialize the template if resizable is used
13843         var tpl = Roo.DomHelper.createTemplate(
13844             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13845         );
13846         tpl.compile();
13847         Roo.Resizable.Handle.prototype.tpl = tpl;
13848     }
13849     this.position = pos;
13850     this.rz = rz;
13851     // show north drag fro topdra
13852     var handlepos = pos == 'hdrag' ? 'north' : pos;
13853     
13854     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13855     if (pos == 'hdrag') {
13856         this.el.setStyle('cursor', 'pointer');
13857     }
13858     this.el.unselectable();
13859     if(transparent){
13860         this.el.setOpacity(0);
13861     }
13862     this.el.on("mousedown", this.onMouseDown, this);
13863     if(!disableTrackOver){
13864         this.el.on("mouseover", this.onMouseOver, this);
13865         this.el.on("mouseout", this.onMouseOut, this);
13866     }
13867 };
13868
13869 // private
13870 Roo.Resizable.Handle.prototype = {
13871     afterResize : function(rz){
13872         // do nothing
13873     },
13874     // private
13875     onMouseDown : function(e){
13876         this.rz.onMouseDown(this, e);
13877     },
13878     // private
13879     onMouseOver : function(e){
13880         this.rz.handleOver(this, e);
13881     },
13882     // private
13883     onMouseOut : function(e){
13884         this.rz.handleOut(this, e);
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898  * @class Roo.Editor
13899  * @extends Roo.Component
13900  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13901  * @constructor
13902  * Create a new Editor
13903  * @param {Roo.form.Field} field The Field object (or descendant)
13904  * @param {Object} config The config object
13905  */
13906 Roo.Editor = function(field, config){
13907     Roo.Editor.superclass.constructor.call(this, config);
13908     this.field = field;
13909     this.addEvents({
13910         /**
13911              * @event beforestartedit
13912              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13913              * false from the handler of this event.
13914              * @param {Editor} this
13915              * @param {Roo.Element} boundEl The underlying element bound to this editor
13916              * @param {Mixed} value The field value being set
13917              */
13918         "beforestartedit" : true,
13919         /**
13920              * @event startedit
13921              * Fires when this editor is displayed
13922              * @param {Roo.Element} boundEl The underlying element bound to this editor
13923              * @param {Mixed} value The starting field value
13924              */
13925         "startedit" : true,
13926         /**
13927              * @event beforecomplete
13928              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13929              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13930              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13931              * event will not fire since no edit actually occurred.
13932              * @param {Editor} this
13933              * @param {Mixed} value The current field value
13934              * @param {Mixed} startValue The original field value
13935              */
13936         "beforecomplete" : true,
13937         /**
13938              * @event complete
13939              * Fires after editing is complete and any changed value has been written to the underlying field.
13940              * @param {Editor} this
13941              * @param {Mixed} value The current field value
13942              * @param {Mixed} startValue The original field value
13943              */
13944         "complete" : true,
13945         /**
13946          * @event specialkey
13947          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13948          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13949          * @param {Roo.form.Field} this
13950          * @param {Roo.EventObject} e The event object
13951          */
13952         "specialkey" : true
13953     });
13954 };
13955
13956 Roo.extend(Roo.Editor, Roo.Component, {
13957     /**
13958      * @cfg {Boolean/String} autosize
13959      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13960      * or "height" to adopt the height only (defaults to false)
13961      */
13962     /**
13963      * @cfg {Boolean} revertInvalid
13964      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13965      * validation fails (defaults to true)
13966      */
13967     /**
13968      * @cfg {Boolean} ignoreNoChange
13969      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13970      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13971      * will never be ignored.
13972      */
13973     /**
13974      * @cfg {Boolean} hideEl
13975      * False to keep the bound element visible while the editor is displayed (defaults to true)
13976      */
13977     /**
13978      * @cfg {Mixed} value
13979      * The data value of the underlying field (defaults to "")
13980      */
13981     value : "",
13982     /**
13983      * @cfg {String} alignment
13984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13985      */
13986     alignment: "c-c?",
13987     /**
13988      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13989      * for bottom-right shadow (defaults to "frame")
13990      */
13991     shadow : "frame",
13992     /**
13993      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13994      */
13995     constrain : false,
13996     /**
13997      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13998      */
13999     completeOnEnter : false,
14000     /**
14001      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14002      */
14003     cancelOnEsc : false,
14004     /**
14005      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14006      */
14007     updateEl : false,
14008
14009     // private
14010     onRender : function(ct, position){
14011         this.el = new Roo.Layer({
14012             shadow: this.shadow,
14013             cls: "x-editor",
14014             parentEl : ct,
14015             shim : this.shim,
14016             shadowOffset:4,
14017             id: this.id,
14018             constrain: this.constrain
14019         });
14020         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14021         if(this.field.msgTarget != 'title'){
14022             this.field.msgTarget = 'qtip';
14023         }
14024         this.field.render(this.el);
14025         if(Roo.isGecko){
14026             this.field.el.dom.setAttribute('autocomplete', 'off');
14027         }
14028         this.field.on("specialkey", this.onSpecialKey, this);
14029         if(this.swallowKeys){
14030             this.field.el.swallowEvent(['keydown','keypress']);
14031         }
14032         this.field.show();
14033         this.field.on("blur", this.onBlur, this);
14034         if(this.field.grow){
14035             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14036         }
14037     },
14038
14039     onSpecialKey : function(field, e)
14040     {
14041         //Roo.log('editor onSpecialKey');
14042         if(this.completeOnEnter && e.getKey() == e.ENTER){
14043             e.stopEvent();
14044             this.completeEdit();
14045             return;
14046         }
14047         // do not fire special key otherwise it might hide close the editor...
14048         if(e.getKey() == e.ENTER){    
14049             return;
14050         }
14051         if(this.cancelOnEsc && e.getKey() == e.ESC){
14052             this.cancelEdit();
14053             return;
14054         } 
14055         this.fireEvent('specialkey', field, e);
14056     
14057     },
14058
14059     /**
14060      * Starts the editing process and shows the editor.
14061      * @param {String/HTMLElement/Element} el The element to edit
14062      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14063       * to the innerHTML of el.
14064      */
14065     startEdit : function(el, value){
14066         if(this.editing){
14067             this.completeEdit();
14068         }
14069         this.boundEl = Roo.get(el);
14070         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14071         if(!this.rendered){
14072             this.render(this.parentEl || document.body);
14073         }
14074         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14075             return;
14076         }
14077         this.startValue = v;
14078         this.field.setValue(v);
14079         if(this.autoSize){
14080             var sz = this.boundEl.getSize();
14081             switch(this.autoSize){
14082                 case "width":
14083                 this.setSize(sz.width,  "");
14084                 break;
14085                 case "height":
14086                 this.setSize("",  sz.height);
14087                 break;
14088                 default:
14089                 this.setSize(sz.width,  sz.height);
14090             }
14091         }
14092         this.el.alignTo(this.boundEl, this.alignment);
14093         this.editing = true;
14094         if(Roo.QuickTips){
14095             Roo.QuickTips.disable();
14096         }
14097         this.show();
14098     },
14099
14100     /**
14101      * Sets the height and width of this editor.
14102      * @param {Number} width The new width
14103      * @param {Number} height The new height
14104      */
14105     setSize : function(w, h){
14106         this.field.setSize(w, h);
14107         if(this.el){
14108             this.el.sync();
14109         }
14110     },
14111
14112     /**
14113      * Realigns the editor to the bound field based on the current alignment config value.
14114      */
14115     realign : function(){
14116         this.el.alignTo(this.boundEl, this.alignment);
14117     },
14118
14119     /**
14120      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14121      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14122      */
14123     completeEdit : function(remainVisible){
14124         if(!this.editing){
14125             return;
14126         }
14127         var v = this.getValue();
14128         if(this.revertInvalid !== false && !this.field.isValid()){
14129             v = this.startValue;
14130             this.cancelEdit(true);
14131         }
14132         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14133             this.editing = false;
14134             this.hide();
14135             return;
14136         }
14137         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14138             this.editing = false;
14139             if(this.updateEl && this.boundEl){
14140                 this.boundEl.update(v);
14141             }
14142             if(remainVisible !== true){
14143                 this.hide();
14144             }
14145             this.fireEvent("complete", this, v, this.startValue);
14146         }
14147     },
14148
14149     // private
14150     onShow : function(){
14151         this.el.show();
14152         if(this.hideEl !== false){
14153             this.boundEl.hide();
14154         }
14155         this.field.show();
14156         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14157             this.fixIEFocus = true;
14158             this.deferredFocus.defer(50, this);
14159         }else{
14160             this.field.focus();
14161         }
14162         this.fireEvent("startedit", this.boundEl, this.startValue);
14163     },
14164
14165     deferredFocus : function(){
14166         if(this.editing){
14167             this.field.focus();
14168         }
14169     },
14170
14171     /**
14172      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14173      * reverted to the original starting value.
14174      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14175      * cancel (defaults to false)
14176      */
14177     cancelEdit : function(remainVisible){
14178         if(this.editing){
14179             this.setValue(this.startValue);
14180             if(remainVisible !== true){
14181                 this.hide();
14182             }
14183         }
14184     },
14185
14186     // private
14187     onBlur : function(){
14188         if(this.allowBlur !== true && this.editing){
14189             this.completeEdit();
14190         }
14191     },
14192
14193     // private
14194     onHide : function(){
14195         if(this.editing){
14196             this.completeEdit();
14197             return;
14198         }
14199         this.field.blur();
14200         if(this.field.collapse){
14201             this.field.collapse();
14202         }
14203         this.el.hide();
14204         if(this.hideEl !== false){
14205             this.boundEl.show();
14206         }
14207         if(Roo.QuickTips){
14208             Roo.QuickTips.enable();
14209         }
14210     },
14211
14212     /**
14213      * Sets the data value of the editor
14214      * @param {Mixed} value Any valid value supported by the underlying field
14215      */
14216     setValue : function(v){
14217         this.field.setValue(v);
14218     },
14219
14220     /**
14221      * Gets the data value of the editor
14222      * @return {Mixed} The data value
14223      */
14224     getValue : function(){
14225         return this.field.getValue();
14226     }
14227 });/*
14228  * Based on:
14229  * Ext JS Library 1.1.1
14230  * Copyright(c) 2006-2007, Ext JS, LLC.
14231  *
14232  * Originally Released Under LGPL - original licence link has changed is not relivant.
14233  *
14234  * Fork - LGPL
14235  * <script type="text/javascript">
14236  */
14237  
14238 /**
14239  * @class Roo.BasicDialog
14240  * @extends Roo.util.Observable
14241  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14242  * <pre><code>
14243 var dlg = new Roo.BasicDialog("my-dlg", {
14244     height: 200,
14245     width: 300,
14246     minHeight: 100,
14247     minWidth: 150,
14248     modal: true,
14249     proxyDrag: true,
14250     shadow: true
14251 });
14252 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14253 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14254 dlg.addButton('Cancel', dlg.hide, dlg);
14255 dlg.show();
14256 </code></pre>
14257   <b>A Dialog should always be a direct child of the body element.</b>
14258  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14259  * @cfg {String} title Default text to display in the title bar (defaults to null)
14260  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14261  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14262  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14263  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14264  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14265  * (defaults to null with no animation)
14266  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14267  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14268  * property for valid values (defaults to 'all')
14269  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14270  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14271  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14272  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14273  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14274  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14275  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14276  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14277  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14278  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14279  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14280  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14281  * draggable = true (defaults to false)
14282  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14283  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14284  * shadow (defaults to false)
14285  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14286  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14287  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14288  * @cfg {Array} buttons Array of buttons
14289  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14290  * @constructor
14291  * Create a new BasicDialog.
14292  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14293  * @param {Object} config Configuration options
14294  */
14295 Roo.BasicDialog = function(el, config){
14296     this.el = Roo.get(el);
14297     var dh = Roo.DomHelper;
14298     if(!this.el && config && config.autoCreate){
14299         if(typeof config.autoCreate == "object"){
14300             if(!config.autoCreate.id){
14301                 config.autoCreate.id = el;
14302             }
14303             this.el = dh.append(document.body,
14304                         config.autoCreate, true);
14305         }else{
14306             this.el = dh.append(document.body,
14307                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14308         }
14309     }
14310     el = this.el;
14311     el.setDisplayed(true);
14312     el.hide = this.hideAction;
14313     this.id = el.id;
14314     el.addClass("x-dlg");
14315
14316     Roo.apply(this, config);
14317
14318     this.proxy = el.createProxy("x-dlg-proxy");
14319     this.proxy.hide = this.hideAction;
14320     this.proxy.setOpacity(.5);
14321     this.proxy.hide();
14322
14323     if(config.width){
14324         el.setWidth(config.width);
14325     }
14326     if(config.height){
14327         el.setHeight(config.height);
14328     }
14329     this.size = el.getSize();
14330     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14331         this.xy = [config.x,config.y];
14332     }else{
14333         this.xy = el.getCenterXY(true);
14334     }
14335     /** The header element @type Roo.Element */
14336     this.header = el.child("> .x-dlg-hd");
14337     /** The body element @type Roo.Element */
14338     this.body = el.child("> .x-dlg-bd");
14339     /** The footer element @type Roo.Element */
14340     this.footer = el.child("> .x-dlg-ft");
14341
14342     if(!this.header){
14343         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14344     }
14345     if(!this.body){
14346         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14347     }
14348
14349     this.header.unselectable();
14350     if(this.title){
14351         this.header.update(this.title);
14352     }
14353     // this element allows the dialog to be focused for keyboard event
14354     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14355     this.focusEl.swallowEvent("click", true);
14356
14357     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14358
14359     // wrap the body and footer for special rendering
14360     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14361     if(this.footer){
14362         this.bwrap.dom.appendChild(this.footer.dom);
14363     }
14364
14365     this.bg = this.el.createChild({
14366         tag: "div", cls:"x-dlg-bg",
14367         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14368     });
14369     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14370
14371
14372     if(this.autoScroll !== false && !this.autoTabs){
14373         this.body.setStyle("overflow", "auto");
14374     }
14375
14376     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14377
14378     if(this.closable !== false){
14379         this.el.addClass("x-dlg-closable");
14380         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14381         this.close.on("click", this.closeClick, this);
14382         this.close.addClassOnOver("x-dlg-close-over");
14383     }
14384     if(this.collapsible !== false){
14385         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14386         this.collapseBtn.on("click", this.collapseClick, this);
14387         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14388         this.header.on("dblclick", this.collapseClick, this);
14389     }
14390     if(this.resizable !== false){
14391         this.el.addClass("x-dlg-resizable");
14392         this.resizer = new Roo.Resizable(el, {
14393             minWidth: this.minWidth || 80,
14394             minHeight:this.minHeight || 80,
14395             handles: this.resizeHandles || "all",
14396             pinned: true
14397         });
14398         this.resizer.on("beforeresize", this.beforeResize, this);
14399         this.resizer.on("resize", this.onResize, this);
14400     }
14401     if(this.draggable !== false){
14402         el.addClass("x-dlg-draggable");
14403         if (!this.proxyDrag) {
14404             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14405         }
14406         else {
14407             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14408         }
14409         dd.setHandleElId(this.header.id);
14410         dd.endDrag = this.endMove.createDelegate(this);
14411         dd.startDrag = this.startMove.createDelegate(this);
14412         dd.onDrag = this.onDrag.createDelegate(this);
14413         dd.scroll = false;
14414         this.dd = dd;
14415     }
14416     if(this.modal){
14417         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14418         this.mask.enableDisplayMode("block");
14419         this.mask.hide();
14420         this.el.addClass("x-dlg-modal");
14421     }
14422     if(this.shadow){
14423         this.shadow = new Roo.Shadow({
14424             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14425             offset : this.shadowOffset
14426         });
14427     }else{
14428         this.shadowOffset = 0;
14429     }
14430     if(Roo.useShims && this.shim !== false){
14431         this.shim = this.el.createShim();
14432         this.shim.hide = this.hideAction;
14433         this.shim.hide();
14434     }else{
14435         this.shim = false;
14436     }
14437     if(this.autoTabs){
14438         this.initTabs();
14439     }
14440     if (this.buttons) { 
14441         var bts= this.buttons;
14442         this.buttons = [];
14443         Roo.each(bts, function(b) {
14444             this.addButton(b);
14445         }, this);
14446     }
14447     
14448     
14449     this.addEvents({
14450         /**
14451          * @event keydown
14452          * Fires when a key is pressed
14453          * @param {Roo.BasicDialog} this
14454          * @param {Roo.EventObject} e
14455          */
14456         "keydown" : true,
14457         /**
14458          * @event move
14459          * Fires when this dialog is moved by the user.
14460          * @param {Roo.BasicDialog} this
14461          * @param {Number} x The new page X
14462          * @param {Number} y The new page Y
14463          */
14464         "move" : true,
14465         /**
14466          * @event resize
14467          * Fires when this dialog is resized by the user.
14468          * @param {Roo.BasicDialog} this
14469          * @param {Number} width The new width
14470          * @param {Number} height The new height
14471          */
14472         "resize" : true,
14473         /**
14474          * @event beforehide
14475          * Fires before this dialog is hidden.
14476          * @param {Roo.BasicDialog} this
14477          */
14478         "beforehide" : true,
14479         /**
14480          * @event hide
14481          * Fires when this dialog is hidden.
14482          * @param {Roo.BasicDialog} this
14483          */
14484         "hide" : true,
14485         /**
14486          * @event beforeshow
14487          * Fires before this dialog is shown.
14488          * @param {Roo.BasicDialog} this
14489          */
14490         "beforeshow" : true,
14491         /**
14492          * @event show
14493          * Fires when this dialog is shown.
14494          * @param {Roo.BasicDialog} this
14495          */
14496         "show" : true
14497     });
14498     el.on("keydown", this.onKeyDown, this);
14499     el.on("mousedown", this.toFront, this);
14500     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14501     this.el.hide();
14502     Roo.DialogManager.register(this);
14503     Roo.BasicDialog.superclass.constructor.call(this);
14504 };
14505
14506 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14507     shadowOffset: Roo.isIE ? 6 : 5,
14508     minHeight: 80,
14509     minWidth: 200,
14510     minButtonWidth: 75,
14511     defaultButton: null,
14512     buttonAlign: "right",
14513     tabTag: 'div',
14514     firstShow: true,
14515
14516     /**
14517      * Sets the dialog title text
14518      * @param {String} text The title text to display
14519      * @return {Roo.BasicDialog} this
14520      */
14521     setTitle : function(text){
14522         this.header.update(text);
14523         return this;
14524     },
14525
14526     // private
14527     closeClick : function(){
14528         this.hide();
14529     },
14530
14531     // private
14532     collapseClick : function(){
14533         this[this.collapsed ? "expand" : "collapse"]();
14534     },
14535
14536     /**
14537      * Collapses the dialog to its minimized state (only the title bar is visible).
14538      * Equivalent to the user clicking the collapse dialog button.
14539      */
14540     collapse : function(){
14541         if(!this.collapsed){
14542             this.collapsed = true;
14543             this.el.addClass("x-dlg-collapsed");
14544             this.restoreHeight = this.el.getHeight();
14545             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14546         }
14547     },
14548
14549     /**
14550      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14551      * clicking the expand dialog button.
14552      */
14553     expand : function(){
14554         if(this.collapsed){
14555             this.collapsed = false;
14556             this.el.removeClass("x-dlg-collapsed");
14557             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14558         }
14559     },
14560
14561     /**
14562      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14563      * @return {Roo.TabPanel} The tabs component
14564      */
14565     initTabs : function(){
14566         var tabs = this.getTabs();
14567         while(tabs.getTab(0)){
14568             tabs.removeTab(0);
14569         }
14570         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14571             var dom = el.dom;
14572             tabs.addTab(Roo.id(dom), dom.title);
14573             dom.title = "";
14574         });
14575         tabs.activate(0);
14576         return tabs;
14577     },
14578
14579     // private
14580     beforeResize : function(){
14581         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14582     },
14583
14584     // private
14585     onResize : function(){
14586         this.refreshSize();
14587         this.syncBodyHeight();
14588         this.adjustAssets();
14589         this.focus();
14590         this.fireEvent("resize", this, this.size.width, this.size.height);
14591     },
14592
14593     // private
14594     onKeyDown : function(e){
14595         if(this.isVisible()){
14596             this.fireEvent("keydown", this, e);
14597         }
14598     },
14599
14600     /**
14601      * Resizes the dialog.
14602      * @param {Number} width
14603      * @param {Number} height
14604      * @return {Roo.BasicDialog} this
14605      */
14606     resizeTo : function(width, height){
14607         this.el.setSize(width, height);
14608         this.size = {width: width, height: height};
14609         this.syncBodyHeight();
14610         if(this.fixedcenter){
14611             this.center();
14612         }
14613         if(this.isVisible()){
14614             this.constrainXY();
14615             this.adjustAssets();
14616         }
14617         this.fireEvent("resize", this, width, height);
14618         return this;
14619     },
14620
14621
14622     /**
14623      * Resizes the dialog to fit the specified content size.
14624      * @param {Number} width
14625      * @param {Number} height
14626      * @return {Roo.BasicDialog} this
14627      */
14628     setContentSize : function(w, h){
14629         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14630         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14631         //if(!this.el.isBorderBox()){
14632             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14633             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14634         //}
14635         if(this.tabs){
14636             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14637             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14638         }
14639         this.resizeTo(w, h);
14640         return this;
14641     },
14642
14643     /**
14644      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14645      * executed in response to a particular key being pressed while the dialog is active.
14646      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14647      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14648      * @param {Function} fn The function to call
14649      * @param {Object} scope (optional) The scope of the function
14650      * @return {Roo.BasicDialog} this
14651      */
14652     addKeyListener : function(key, fn, scope){
14653         var keyCode, shift, ctrl, alt;
14654         if(typeof key == "object" && !(key instanceof Array)){
14655             keyCode = key["key"];
14656             shift = key["shift"];
14657             ctrl = key["ctrl"];
14658             alt = key["alt"];
14659         }else{
14660             keyCode = key;
14661         }
14662         var handler = function(dlg, e){
14663             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14664                 var k = e.getKey();
14665                 if(keyCode instanceof Array){
14666                     for(var i = 0, len = keyCode.length; i < len; i++){
14667                         if(keyCode[i] == k){
14668                           fn.call(scope || window, dlg, k, e);
14669                           return;
14670                         }
14671                     }
14672                 }else{
14673                     if(k == keyCode){
14674                         fn.call(scope || window, dlg, k, e);
14675                     }
14676                 }
14677             }
14678         };
14679         this.on("keydown", handler);
14680         return this;
14681     },
14682
14683     /**
14684      * Returns the TabPanel component (creates it if it doesn't exist).
14685      * Note: If you wish to simply check for the existence of tabs without creating them,
14686      * check for a null 'tabs' property.
14687      * @return {Roo.TabPanel} The tabs component
14688      */
14689     getTabs : function(){
14690         if(!this.tabs){
14691             this.el.addClass("x-dlg-auto-tabs");
14692             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14693             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14694         }
14695         return this.tabs;
14696     },
14697
14698     /**
14699      * Adds a button to the footer section of the dialog.
14700      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14701      * object or a valid Roo.DomHelper element config
14702      * @param {Function} handler The function called when the button is clicked
14703      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14704      * @return {Roo.Button} The new button
14705      */
14706     addButton : function(config, handler, scope){
14707         var dh = Roo.DomHelper;
14708         if(!this.footer){
14709             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14710         }
14711         if(!this.btnContainer){
14712             var tb = this.footer.createChild({
14713
14714                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14715                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14716             }, null, true);
14717             this.btnContainer = tb.firstChild.firstChild.firstChild;
14718         }
14719         var bconfig = {
14720             handler: handler,
14721             scope: scope,
14722             minWidth: this.minButtonWidth,
14723             hideParent:true
14724         };
14725         if(typeof config == "string"){
14726             bconfig.text = config;
14727         }else{
14728             if(config.tag){
14729                 bconfig.dhconfig = config;
14730             }else{
14731                 Roo.apply(bconfig, config);
14732             }
14733         }
14734         var fc = false;
14735         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14736             bconfig.position = Math.max(0, bconfig.position);
14737             fc = this.btnContainer.childNodes[bconfig.position];
14738         }
14739          
14740         var btn = new Roo.Button(
14741             fc ? 
14742                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14743                 : this.btnContainer.appendChild(document.createElement("td")),
14744             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14745             bconfig
14746         );
14747         this.syncBodyHeight();
14748         if(!this.buttons){
14749             /**
14750              * Array of all the buttons that have been added to this dialog via addButton
14751              * @type Array
14752              */
14753             this.buttons = [];
14754         }
14755         this.buttons.push(btn);
14756         return btn;
14757     },
14758
14759     /**
14760      * Sets the default button to be focused when the dialog is displayed.
14761      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14762      * @return {Roo.BasicDialog} this
14763      */
14764     setDefaultButton : function(btn){
14765         this.defaultButton = btn;
14766         return this;
14767     },
14768
14769     // private
14770     getHeaderFooterHeight : function(safe){
14771         var height = 0;
14772         if(this.header){
14773            height += this.header.getHeight();
14774         }
14775         if(this.footer){
14776            var fm = this.footer.getMargins();
14777             height += (this.footer.getHeight()+fm.top+fm.bottom);
14778         }
14779         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14780         height += this.centerBg.getPadding("tb");
14781         return height;
14782     },
14783
14784     // private
14785     syncBodyHeight : function(){
14786         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14787         var height = this.size.height - this.getHeaderFooterHeight(false);
14788         bd.setHeight(height-bd.getMargins("tb"));
14789         var hh = this.header.getHeight();
14790         var h = this.size.height-hh;
14791         cb.setHeight(h);
14792         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14793         bw.setHeight(h-cb.getPadding("tb"));
14794         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14795         bd.setWidth(bw.getWidth(true));
14796         if(this.tabs){
14797             this.tabs.syncHeight();
14798             if(Roo.isIE){
14799                 this.tabs.el.repaint();
14800             }
14801         }
14802     },
14803
14804     /**
14805      * Restores the previous state of the dialog if Roo.state is configured.
14806      * @return {Roo.BasicDialog} this
14807      */
14808     restoreState : function(){
14809         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14810         if(box && box.width){
14811             this.xy = [box.x, box.y];
14812             this.resizeTo(box.width, box.height);
14813         }
14814         return this;
14815     },
14816
14817     // private
14818     beforeShow : function(){
14819         this.expand();
14820         if(this.fixedcenter){
14821             this.xy = this.el.getCenterXY(true);
14822         }
14823         if(this.modal){
14824             Roo.get(document.body).addClass("x-body-masked");
14825             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14826             this.mask.show();
14827         }
14828         this.constrainXY();
14829     },
14830
14831     // private
14832     animShow : function(){
14833         var b = Roo.get(this.animateTarget).getBox();
14834         this.proxy.setSize(b.width, b.height);
14835         this.proxy.setLocation(b.x, b.y);
14836         this.proxy.show();
14837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14838                     true, .35, this.showEl.createDelegate(this));
14839     },
14840
14841     /**
14842      * Shows the dialog.
14843      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14844      * @return {Roo.BasicDialog} this
14845      */
14846     show : function(animateTarget){
14847         if (this.fireEvent("beforeshow", this) === false){
14848             return;
14849         }
14850         if(this.syncHeightBeforeShow){
14851             this.syncBodyHeight();
14852         }else if(this.firstShow){
14853             this.firstShow = false;
14854             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14855         }
14856         this.animateTarget = animateTarget || this.animateTarget;
14857         if(!this.el.isVisible()){
14858             this.beforeShow();
14859             if(this.animateTarget && Roo.get(this.animateTarget)){
14860                 this.animShow();
14861             }else{
14862                 this.showEl();
14863             }
14864         }
14865         return this;
14866     },
14867
14868     // private
14869     showEl : function(){
14870         this.proxy.hide();
14871         this.el.setXY(this.xy);
14872         this.el.show();
14873         this.adjustAssets(true);
14874         this.toFront();
14875         this.focus();
14876         // IE peekaboo bug - fix found by Dave Fenwick
14877         if(Roo.isIE){
14878             this.el.repaint();
14879         }
14880         this.fireEvent("show", this);
14881     },
14882
14883     /**
14884      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14885      * dialog itself will receive focus.
14886      */
14887     focus : function(){
14888         if(this.defaultButton){
14889             this.defaultButton.focus();
14890         }else{
14891             this.focusEl.focus();
14892         }
14893     },
14894
14895     // private
14896     constrainXY : function(){
14897         if(this.constraintoviewport !== false){
14898             if(!this.viewSize){
14899                 if(this.container){
14900                     var s = this.container.getSize();
14901                     this.viewSize = [s.width, s.height];
14902                 }else{
14903                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14904                 }
14905             }
14906             var s = Roo.get(this.container||document).getScroll();
14907
14908             var x = this.xy[0], y = this.xy[1];
14909             var w = this.size.width, h = this.size.height;
14910             var vw = this.viewSize[0], vh = this.viewSize[1];
14911             // only move it if it needs it
14912             var moved = false;
14913             // first validate right/bottom
14914             if(x + w > vw+s.left){
14915                 x = vw - w;
14916                 moved = true;
14917             }
14918             if(y + h > vh+s.top){
14919                 y = vh - h;
14920                 moved = true;
14921             }
14922             // then make sure top/left isn't negative
14923             if(x < s.left){
14924                 x = s.left;
14925                 moved = true;
14926             }
14927             if(y < s.top){
14928                 y = s.top;
14929                 moved = true;
14930             }
14931             if(moved){
14932                 // cache xy
14933                 this.xy = [x, y];
14934                 if(this.isVisible()){
14935                     this.el.setLocation(x, y);
14936                     this.adjustAssets();
14937                 }
14938             }
14939         }
14940     },
14941
14942     // private
14943     onDrag : function(){
14944         if(!this.proxyDrag){
14945             this.xy = this.el.getXY();
14946             this.adjustAssets();
14947         }
14948     },
14949
14950     // private
14951     adjustAssets : function(doShow){
14952         var x = this.xy[0], y = this.xy[1];
14953         var w = this.size.width, h = this.size.height;
14954         if(doShow === true){
14955             if(this.shadow){
14956                 this.shadow.show(this.el);
14957             }
14958             if(this.shim){
14959                 this.shim.show();
14960             }
14961         }
14962         if(this.shadow && this.shadow.isVisible()){
14963             this.shadow.show(this.el);
14964         }
14965         if(this.shim && this.shim.isVisible()){
14966             this.shim.setBounds(x, y, w, h);
14967         }
14968     },
14969
14970     // private
14971     adjustViewport : function(w, h){
14972         if(!w || !h){
14973             w = Roo.lib.Dom.getViewWidth();
14974             h = Roo.lib.Dom.getViewHeight();
14975         }
14976         // cache the size
14977         this.viewSize = [w, h];
14978         if(this.modal && this.mask.isVisible()){
14979             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14980             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981         }
14982         if(this.isVisible()){
14983             this.constrainXY();
14984         }
14985     },
14986
14987     /**
14988      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14989      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14991      */
14992     destroy : function(removeEl){
14993         if(this.isVisible()){
14994             this.animateTarget = null;
14995             this.hide();
14996         }
14997         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14998         if(this.tabs){
14999             this.tabs.destroy(removeEl);
15000         }
15001         Roo.destroy(
15002              this.shim,
15003              this.proxy,
15004              this.resizer,
15005              this.close,
15006              this.mask
15007         );
15008         if(this.dd){
15009             this.dd.unreg();
15010         }
15011         if(this.buttons){
15012            for(var i = 0, len = this.buttons.length; i < len; i++){
15013                this.buttons[i].destroy();
15014            }
15015         }
15016         this.el.removeAllListeners();
15017         if(removeEl === true){
15018             this.el.update("");
15019             this.el.remove();
15020         }
15021         Roo.DialogManager.unregister(this);
15022     },
15023
15024     // private
15025     startMove : function(){
15026         if(this.proxyDrag){
15027             this.proxy.show();
15028         }
15029         if(this.constraintoviewport !== false){
15030             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15031         }
15032     },
15033
15034     // private
15035     endMove : function(){
15036         if(!this.proxyDrag){
15037             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15038         }else{
15039             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15040             this.proxy.hide();
15041         }
15042         this.refreshSize();
15043         this.adjustAssets();
15044         this.focus();
15045         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15046     },
15047
15048     /**
15049      * Brings this dialog to the front of any other visible dialogs
15050      * @return {Roo.BasicDialog} this
15051      */
15052     toFront : function(){
15053         Roo.DialogManager.bringToFront(this);
15054         return this;
15055     },
15056
15057     /**
15058      * Sends this dialog to the back (under) of any other visible dialogs
15059      * @return {Roo.BasicDialog} this
15060      */
15061     toBack : function(){
15062         Roo.DialogManager.sendToBack(this);
15063         return this;
15064     },
15065
15066     /**
15067      * Centers this dialog in the viewport
15068      * @return {Roo.BasicDialog} this
15069      */
15070     center : function(){
15071         var xy = this.el.getCenterXY(true);
15072         this.moveTo(xy[0], xy[1]);
15073         return this;
15074     },
15075
15076     /**
15077      * Moves the dialog's top-left corner to the specified point
15078      * @param {Number} x
15079      * @param {Number} y
15080      * @return {Roo.BasicDialog} this
15081      */
15082     moveTo : function(x, y){
15083         this.xy = [x,y];
15084         if(this.isVisible()){
15085             this.el.setXY(this.xy);
15086             this.adjustAssets();
15087         }
15088         return this;
15089     },
15090
15091     /**
15092      * Aligns the dialog to the specified element
15093      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15094      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15095      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15096      * @return {Roo.BasicDialog} this
15097      */
15098     alignTo : function(element, position, offsets){
15099         this.xy = this.el.getAlignToXY(element, position, offsets);
15100         if(this.isVisible()){
15101             this.el.setXY(this.xy);
15102             this.adjustAssets();
15103         }
15104         return this;
15105     },
15106
15107     /**
15108      * Anchors an element to another element and realigns it when the window is resized.
15109      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15110      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15111      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15112      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15113      * is a number, it is used as the buffer delay (defaults to 50ms).
15114      * @return {Roo.BasicDialog} this
15115      */
15116     anchorTo : function(el, alignment, offsets, monitorScroll){
15117         var action = function(){
15118             this.alignTo(el, alignment, offsets);
15119         };
15120         Roo.EventManager.onWindowResize(action, this);
15121         var tm = typeof monitorScroll;
15122         if(tm != 'undefined'){
15123             Roo.EventManager.on(window, 'scroll', action, this,
15124                 {buffer: tm == 'number' ? monitorScroll : 50});
15125         }
15126         action.call(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Returns true if the dialog is visible
15132      * @return {Boolean}
15133      */
15134     isVisible : function(){
15135         return this.el.isVisible();
15136     },
15137
15138     // private
15139     animHide : function(callback){
15140         var b = Roo.get(this.animateTarget).getBox();
15141         this.proxy.show();
15142         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15143         this.el.hide();
15144         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15145                     this.hideEl.createDelegate(this, [callback]));
15146     },
15147
15148     /**
15149      * Hides the dialog.
15150      * @param {Function} callback (optional) Function to call when the dialog is hidden
15151      * @return {Roo.BasicDialog} this
15152      */
15153     hide : function(callback){
15154         if (this.fireEvent("beforehide", this) === false){
15155             return;
15156         }
15157         if(this.shadow){
15158             this.shadow.hide();
15159         }
15160         if(this.shim) {
15161           this.shim.hide();
15162         }
15163         // sometimes animateTarget seems to get set.. causing problems...
15164         // this just double checks..
15165         if(this.animateTarget && Roo.get(this.animateTarget)) {
15166            this.animHide(callback);
15167         }else{
15168             this.el.hide();
15169             this.hideEl(callback);
15170         }
15171         return this;
15172     },
15173
15174     // private
15175     hideEl : function(callback){
15176         this.proxy.hide();
15177         if(this.modal){
15178             this.mask.hide();
15179             Roo.get(document.body).removeClass("x-body-masked");
15180         }
15181         this.fireEvent("hide", this);
15182         if(typeof callback == "function"){
15183             callback();
15184         }
15185     },
15186
15187     // private
15188     hideAction : function(){
15189         this.setLeft("-10000px");
15190         this.setTop("-10000px");
15191         this.setStyle("visibility", "hidden");
15192     },
15193
15194     // private
15195     refreshSize : function(){
15196         this.size = this.el.getSize();
15197         this.xy = this.el.getXY();
15198         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15199     },
15200
15201     // private
15202     // z-index is managed by the DialogManager and may be overwritten at any time
15203     setZIndex : function(index){
15204         if(this.modal){
15205             this.mask.setStyle("z-index", index);
15206         }
15207         if(this.shim){
15208             this.shim.setStyle("z-index", ++index);
15209         }
15210         if(this.shadow){
15211             this.shadow.setZIndex(++index);
15212         }
15213         this.el.setStyle("z-index", ++index);
15214         if(this.proxy){
15215             this.proxy.setStyle("z-index", ++index);
15216         }
15217         if(this.resizer){
15218             this.resizer.proxy.setStyle("z-index", ++index);
15219         }
15220
15221         this.lastZIndex = index;
15222     },
15223
15224     /**
15225      * Returns the element for this dialog
15226      * @return {Roo.Element} The underlying dialog Element
15227      */
15228     getEl : function(){
15229         return this.el;
15230     }
15231 });
15232
15233 /**
15234  * @class Roo.DialogManager
15235  * Provides global access to BasicDialogs that have been created and
15236  * support for z-indexing (layering) multiple open dialogs.
15237  */
15238 Roo.DialogManager = function(){
15239     var list = {};
15240     var accessList = [];
15241     var front = null;
15242
15243     // private
15244     var sortDialogs = function(d1, d2){
15245         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15246     };
15247
15248     // private
15249     var orderDialogs = function(){
15250         accessList.sort(sortDialogs);
15251         var seed = Roo.DialogManager.zseed;
15252         for(var i = 0, len = accessList.length; i < len; i++){
15253             var dlg = accessList[i];
15254             if(dlg){
15255                 dlg.setZIndex(seed + (i*10));
15256             }
15257         }
15258     };
15259
15260     return {
15261         /**
15262          * The starting z-index for BasicDialogs (defaults to 9000)
15263          * @type Number The z-index value
15264          */
15265         zseed : 9000,
15266
15267         // private
15268         register : function(dlg){
15269             list[dlg.id] = dlg;
15270             accessList.push(dlg);
15271         },
15272
15273         // private
15274         unregister : function(dlg){
15275             delete list[dlg.id];
15276             var i=0;
15277             var len=0;
15278             if(!accessList.indexOf){
15279                 for(  i = 0, len = accessList.length; i < len; i++){
15280                     if(accessList[i] == dlg){
15281                         accessList.splice(i, 1);
15282                         return;
15283                     }
15284                 }
15285             }else{
15286                  i = accessList.indexOf(dlg);
15287                 if(i != -1){
15288                     accessList.splice(i, 1);
15289                 }
15290             }
15291         },
15292
15293         /**
15294          * Gets a registered dialog by id
15295          * @param {String/Object} id The id of the dialog or a dialog
15296          * @return {Roo.BasicDialog} this
15297          */
15298         get : function(id){
15299             return typeof id == "object" ? id : list[id];
15300         },
15301
15302         /**
15303          * Brings the specified dialog to the front
15304          * @param {String/Object} dlg The id of the dialog or a dialog
15305          * @return {Roo.BasicDialog} this
15306          */
15307         bringToFront : function(dlg){
15308             dlg = this.get(dlg);
15309             if(dlg != front){
15310                 front = dlg;
15311                 dlg._lastAccess = new Date().getTime();
15312                 orderDialogs();
15313             }
15314             return dlg;
15315         },
15316
15317         /**
15318          * Sends the specified dialog to the back
15319          * @param {String/Object} dlg The id of the dialog or a dialog
15320          * @return {Roo.BasicDialog} this
15321          */
15322         sendToBack : function(dlg){
15323             dlg = this.get(dlg);
15324             dlg._lastAccess = -(new Date().getTime());
15325             orderDialogs();
15326             return dlg;
15327         },
15328
15329         /**
15330          * Hides all dialogs
15331          */
15332         hideAll : function(){
15333             for(var id in list){
15334                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15335                     list[id].hide();
15336                 }
15337             }
15338         }
15339     };
15340 }();
15341
15342 /**
15343  * @class Roo.LayoutDialog
15344  * @extends Roo.BasicDialog
15345  * Dialog which provides adjustments for working with a layout in a Dialog.
15346  * Add your necessary layout config options to the dialog's config.<br>
15347  * Example usage (including a nested layout):
15348  * <pre><code>
15349 if(!dialog){
15350     dialog = new Roo.LayoutDialog("download-dlg", {
15351         modal: true,
15352         width:600,
15353         height:450,
15354         shadow:true,
15355         minWidth:500,
15356         minHeight:350,
15357         autoTabs:true,
15358         proxyDrag:true,
15359         // layout config merges with the dialog config
15360         center:{
15361             tabPosition: "top",
15362             alwaysShowTabs: true
15363         }
15364     });
15365     dialog.addKeyListener(27, dialog.hide, dialog);
15366     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15367     dialog.addButton("Build It!", this.getDownload, this);
15368
15369     // we can even add nested layouts
15370     var innerLayout = new Roo.BorderLayout("dl-inner", {
15371         east: {
15372             initialSize: 200,
15373             autoScroll:true,
15374             split:true
15375         },
15376         center: {
15377             autoScroll:true
15378         }
15379     });
15380     innerLayout.beginUpdate();
15381     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15382     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15383     innerLayout.endUpdate(true);
15384
15385     var layout = dialog.getLayout();
15386     layout.beginUpdate();
15387     layout.add("center", new Roo.ContentPanel("standard-panel",
15388                         {title: "Download the Source", fitToFrame:true}));
15389     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15390                {title: "Build your own roo.js"}));
15391     layout.getRegion("center").showPanel(sp);
15392     layout.endUpdate();
15393 }
15394 </code></pre>
15395     * @constructor
15396     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15397     * @param {Object} config configuration options
15398   */
15399 Roo.LayoutDialog = function(el, cfg){
15400     
15401     var config=  cfg;
15402     if (typeof(cfg) == 'undefined') {
15403         config = Roo.apply({}, el);
15404         // not sure why we use documentElement here.. - it should always be body.
15405         // IE7 borks horribly if we use documentElement.
15406         // webkit also does not like documentElement - it creates a body element...
15407         el = Roo.get( document.body || document.documentElement ).createChild();
15408         //config.autoCreate = true;
15409     }
15410     
15411     
15412     config.autoTabs = false;
15413     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15414     this.body.setStyle({overflow:"hidden", position:"relative"});
15415     this.layout = new Roo.BorderLayout(this.body.dom, config);
15416     this.layout.monitorWindowResize = false;
15417     this.el.addClass("x-dlg-auto-layout");
15418     // fix case when center region overwrites center function
15419     this.center = Roo.BasicDialog.prototype.center;
15420     this.on("show", this.layout.layout, this.layout, true);
15421     if (config.items) {
15422         var xitems = config.items;
15423         delete config.items;
15424         Roo.each(xitems, this.addxtype, this);
15425     }
15426     
15427     
15428 };
15429 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15430     /**
15431      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15432      * @deprecated
15433      */
15434     endUpdate : function(){
15435         this.layout.endUpdate();
15436     },
15437
15438     /**
15439      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15440      *  @deprecated
15441      */
15442     beginUpdate : function(){
15443         this.layout.beginUpdate();
15444     },
15445
15446     /**
15447      * Get the BorderLayout for this dialog
15448      * @return {Roo.BorderLayout}
15449      */
15450     getLayout : function(){
15451         return this.layout;
15452     },
15453
15454     showEl : function(){
15455         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15456         if(Roo.isIE7){
15457             this.layout.layout();
15458         }
15459     },
15460
15461     // private
15462     // Use the syncHeightBeforeShow config option to control this automatically
15463     syncBodyHeight : function(){
15464         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15465         if(this.layout){this.layout.layout();}
15466     },
15467     
15468       /**
15469      * Add an xtype element (actually adds to the layout.)
15470      * @return {Object} xdata xtype object data.
15471      */
15472     
15473     addxtype : function(c) {
15474         return this.layout.addxtype(c);
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486  
15487 /**
15488  * @class Roo.MessageBox
15489  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15490  * Example usage:
15491  *<pre><code>
15492 // Basic alert:
15493 Roo.Msg.alert('Status', 'Changes saved successfully.');
15494
15495 // Prompt for user data:
15496 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15497     if (btn == 'ok'){
15498         // process text value...
15499     }
15500 });
15501
15502 // Show a dialog using config options:
15503 Roo.Msg.show({
15504    title:'Save Changes?',
15505    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15506    buttons: Roo.Msg.YESNOCANCEL,
15507    fn: processResult,
15508    animEl: 'elId'
15509 });
15510 </code></pre>
15511  * @singleton
15512  */
15513 Roo.MessageBox = function(){
15514     var dlg, opt, mask, waitTimer;
15515     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15516     var buttons, activeTextEl, bwidth;
15517
15518     // private
15519     var handleButton = function(button){
15520         dlg.hide();
15521         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15522     };
15523
15524     // private
15525     var handleHide = function(){
15526         if(opt && opt.cls){
15527             dlg.el.removeClass(opt.cls);
15528         }
15529         if(waitTimer){
15530             Roo.TaskMgr.stop(waitTimer);
15531             waitTimer = null;
15532         }
15533     };
15534
15535     // private
15536     var updateButtons = function(b){
15537         var width = 0;
15538         if(!b){
15539             buttons["ok"].hide();
15540             buttons["cancel"].hide();
15541             buttons["yes"].hide();
15542             buttons["no"].hide();
15543             dlg.footer.dom.style.display = 'none';
15544             return width;
15545         }
15546         dlg.footer.dom.style.display = '';
15547         for(var k in buttons){
15548             if(typeof buttons[k] != "function"){
15549                 if(b[k]){
15550                     buttons[k].show();
15551                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15552                     width += buttons[k].el.getWidth()+15;
15553                 }else{
15554                     buttons[k].hide();
15555                 }
15556             }
15557         }
15558         return width;
15559     };
15560
15561     // private
15562     var handleEsc = function(d, k, e){
15563         if(opt && opt.closable !== false){
15564             dlg.hide();
15565         }
15566         if(e){
15567             e.stopEvent();
15568         }
15569     };
15570
15571     return {
15572         /**
15573          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15574          * @return {Roo.BasicDialog} The BasicDialog element
15575          */
15576         getDialog : function(){
15577            if(!dlg){
15578                 dlg = new Roo.BasicDialog("x-msg-box", {
15579                     autoCreate : true,
15580                     shadow: true,
15581                     draggable: true,
15582                     resizable:false,
15583                     constraintoviewport:false,
15584                     fixedcenter:true,
15585                     collapsible : false,
15586                     shim:true,
15587                     modal: true,
15588                     width:400, height:100,
15589                     buttonAlign:"center",
15590                     closeClick : function(){
15591                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15592                             handleButton("no");
15593                         }else{
15594                             handleButton("cancel");
15595                         }
15596                     }
15597                 });
15598                 dlg.on("hide", handleHide);
15599                 mask = dlg.mask;
15600                 dlg.addKeyListener(27, handleEsc);
15601                 buttons = {};
15602                 var bt = this.buttonText;
15603                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15604                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15605                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15606                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15607                 bodyEl = dlg.body.createChild({
15608
15609                     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>'
15610                 });
15611                 msgEl = bodyEl.dom.firstChild;
15612                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15613                 textboxEl.enableDisplayMode();
15614                 textboxEl.addKeyListener([10,13], function(){
15615                     if(dlg.isVisible() && opt && opt.buttons){
15616                         if(opt.buttons.ok){
15617                             handleButton("ok");
15618                         }else if(opt.buttons.yes){
15619                             handleButton("yes");
15620                         }
15621                     }
15622                 });
15623                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15624                 textareaEl.enableDisplayMode();
15625                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15626                 progressEl.enableDisplayMode();
15627                 var pf = progressEl.dom.firstChild;
15628                 if (pf) {
15629                     pp = Roo.get(pf.firstChild);
15630                     pp.setHeight(pf.offsetHeight);
15631                 }
15632                 
15633             }
15634             return dlg;
15635         },
15636
15637         /**
15638          * Updates the message box body text
15639          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15640          * the XHTML-compliant non-breaking space character '&amp;#160;')
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         updateText : function(text){
15644             if(!dlg.isVisible() && !opt.width){
15645                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15646             }
15647             msgEl.innerHTML = text || '&#160;';
15648             var cw =  Math.max(msgEl.offsetWidth, msgEl.scrollWidth);
15649             Roo.log("guesed size: " + cw);
15650             var w = Math.max(
15651                     Math.min(opt.width || cw , this.maxWidth), 
15652                     Math.max(opt.minWidth || this.minWidth, bwidth)
15653             );
15654             if(opt.prompt){
15655                 activeTextEl.setWidth(w);
15656             }
15657             if(dlg.isVisible()){
15658                 dlg.fixedcenter = false;
15659             }
15660             // to big, make it scoll.
15661             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15662                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15663                 bodyEl.dom.style.overflowY = 'auto !important';
15664             } else {
15665                 bodyEl.dom.style.height = '';
15666                 bodyEl.dom.style.overflowY = '';
15667             }
15668             if (cw > w) {
15669                 bodyEl.dom.style.overflowX = 'auto !important';
15670             } else {
15671                 bodyEl.dom.style.overflowX = '';
15672             }
15673             
15674             dlg.setContentSize(w, bodyEl.getHeight());
15675             if(dlg.isVisible()){
15676                 dlg.fixedcenter = true;
15677             }
15678             return this;
15679         },
15680
15681         /**
15682          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15683          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15684          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15685          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15686          * @return {Roo.MessageBox} This message box
15687          */
15688         updateProgress : function(value, text){
15689             if(text){
15690                 this.updateText(text);
15691             }
15692             if (pp) { // weird bug on my firefox - for some reason this is not defined
15693                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15694             }
15695             return this;
15696         },        
15697
15698         /**
15699          * Returns true if the message box is currently displayed
15700          * @return {Boolean} True if the message box is visible, else false
15701          */
15702         isVisible : function(){
15703             return dlg && dlg.isVisible();  
15704         },
15705
15706         /**
15707          * Hides the message box if it is displayed
15708          */
15709         hide : function(){
15710             if(this.isVisible()){
15711                 dlg.hide();
15712             }  
15713         },
15714
15715         /**
15716          * Displays a new message box, or reinitializes an existing message box, based on the config options
15717          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15718          * The following config object properties are supported:
15719          * <pre>
15720 Property    Type             Description
15721 ----------  ---------------  ------------------------------------------------------------------------------------
15722 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15723                                    closes (defaults to undefined)
15724 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15725                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15726 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15727                                    progress and wait dialogs will ignore this property and always hide the
15728                                    close button as they can only be closed programmatically.
15729 cls               String           A custom CSS class to apply to the message box element
15730 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15731                                    displayed (defaults to 75)
15732 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15733                                    function will be btn (the name of the button that was clicked, if applicable,
15734                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15735                                    Progress and wait dialogs will ignore this option since they do not respond to
15736                                    user actions and can only be closed programmatically, so any required function
15737                                    should be called by the same code after it closes the dialog.
15738 icon              String           A CSS class that provides a background image to be used as an icon for
15739                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15740 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15741 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15742 modal             Boolean          False to allow user interaction with the page while the message box is
15743                                    displayed (defaults to true)
15744 msg               String           A string that will replace the existing message box body text (defaults
15745                                    to the XHTML-compliant non-breaking space character '&#160;')
15746 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15747 progress          Boolean          True to display a progress bar (defaults to false)
15748 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15749 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15750 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15751 title             String           The title text
15752 value             String           The string value to set into the active textbox element if displayed
15753 wait              Boolean          True to display a progress bar (defaults to false)
15754 width             Number           The width of the dialog in pixels
15755 </pre>
15756          *
15757          * Example usage:
15758          * <pre><code>
15759 Roo.Msg.show({
15760    title: 'Address',
15761    msg: 'Please enter your address:',
15762    width: 300,
15763    buttons: Roo.MessageBox.OKCANCEL,
15764    multiline: true,
15765    fn: saveAddress,
15766    animEl: 'addAddressBtn'
15767 });
15768 </code></pre>
15769          * @param {Object} config Configuration options
15770          * @return {Roo.MessageBox} This message box
15771          */
15772         show : function(options)
15773         {
15774             
15775             // this causes nightmares if you show one dialog after another
15776             // especially on callbacks..
15777              
15778             if(this.isVisible()){
15779                 
15780                 this.hide();
15781                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
15782                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15783                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15784                 
15785             }
15786             var d = this.getDialog();
15787             opt = options;
15788             d.setTitle(opt.title || "&#160;");
15789             d.close.setDisplayed(opt.closable !== false);
15790             activeTextEl = textboxEl;
15791             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15792             if(opt.prompt){
15793                 if(opt.multiline){
15794                     textboxEl.hide();
15795                     textareaEl.show();
15796                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15797                         opt.multiline : this.defaultTextHeight);
15798                     activeTextEl = textareaEl;
15799                 }else{
15800                     textboxEl.show();
15801                     textareaEl.hide();
15802                 }
15803             }else{
15804                 textboxEl.hide();
15805                 textareaEl.hide();
15806             }
15807             progressEl.setDisplayed(opt.progress === true);
15808             this.updateProgress(0);
15809             activeTextEl.dom.value = opt.value || "";
15810             if(opt.prompt){
15811                 dlg.setDefaultButton(activeTextEl);
15812             }else{
15813                 var bs = opt.buttons;
15814                 var db = null;
15815                 if(bs && bs.ok){
15816                     db = buttons["ok"];
15817                 }else if(bs && bs.yes){
15818                     db = buttons["yes"];
15819                 }
15820                 dlg.setDefaultButton(db);
15821             }
15822             bwidth = updateButtons(opt.buttons);
15823             this.updateText(opt.msg);
15824             if(opt.cls){
15825                 d.el.addClass(opt.cls);
15826             }
15827             d.proxyDrag = opt.proxyDrag === true;
15828             d.modal = opt.modal !== false;
15829             d.mask = opt.modal !== false ? mask : false;
15830             if(!d.isVisible()){
15831                 // force it to the end of the z-index stack so it gets a cursor in FF
15832                 document.body.appendChild(dlg.el.dom);
15833                 d.animateTarget = null;
15834                 d.show(options.animEl);
15835             }
15836             return this;
15837         },
15838
15839         /**
15840          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15841          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15842          * and closing the message box when the process is complete.
15843          * @param {String} title The title bar text
15844          * @param {String} msg The message box body text
15845          * @return {Roo.MessageBox} This message box
15846          */
15847         progress : function(title, msg){
15848             this.show({
15849                 title : title,
15850                 msg : msg,
15851                 buttons: false,
15852                 progress:true,
15853                 closable:false,
15854                 minWidth: this.minProgressWidth,
15855                 modal : true
15856             });
15857             return this;
15858         },
15859
15860         /**
15861          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15862          * If a callback function is passed it will be called after the user clicks the button, and the
15863          * id of the button that was clicked will be passed as the only parameter to the callback
15864          * (could also be the top-right close button).
15865          * @param {String} title The title bar text
15866          * @param {String} msg The message box body text
15867          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15868          * @param {Object} scope (optional) The scope of the callback function
15869          * @return {Roo.MessageBox} This message box
15870          */
15871         alert : function(title, msg, fn, scope){
15872             this.show({
15873                 title : title,
15874                 msg : msg,
15875                 buttons: this.OK,
15876                 fn: fn,
15877                 scope : scope,
15878                 modal : true
15879             });
15880             return this;
15881         },
15882
15883         /**
15884          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15885          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15886          * You are responsible for closing the message box when the process is complete.
15887          * @param {String} msg The message box body text
15888          * @param {String} title (optional) The title bar text
15889          * @return {Roo.MessageBox} This message box
15890          */
15891         wait : function(msg, title){
15892             this.show({
15893                 title : title,
15894                 msg : msg,
15895                 buttons: false,
15896                 closable:false,
15897                 progress:true,
15898                 modal:true,
15899                 width:300,
15900                 wait:true
15901             });
15902             waitTimer = Roo.TaskMgr.start({
15903                 run: function(i){
15904                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15905                 },
15906                 interval: 1000
15907             });
15908             return this;
15909         },
15910
15911         /**
15912          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15913          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15914          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15915          * @param {String} title The title bar text
15916          * @param {String} msg The message box body text
15917          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15918          * @param {Object} scope (optional) The scope of the callback function
15919          * @return {Roo.MessageBox} This message box
15920          */
15921         confirm : function(title, msg, fn, scope){
15922             this.show({
15923                 title : title,
15924                 msg : msg,
15925                 buttons: this.YESNO,
15926                 fn: fn,
15927                 scope : scope,
15928                 modal : true
15929             });
15930             return this;
15931         },
15932
15933         /**
15934          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15935          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15936          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15937          * (could also be the top-right close button) and the text that was entered will be passed as the two
15938          * parameters to the callback.
15939          * @param {String} title The title bar text
15940          * @param {String} msg The message box body text
15941          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15942          * @param {Object} scope (optional) The scope of the callback function
15943          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15944          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15945          * @return {Roo.MessageBox} This message box
15946          */
15947         prompt : function(title, msg, fn, scope, multiline){
15948             this.show({
15949                 title : title,
15950                 msg : msg,
15951                 buttons: this.OKCANCEL,
15952                 fn: fn,
15953                 minWidth:250,
15954                 scope : scope,
15955                 prompt:true,
15956                 multiline: multiline,
15957                 modal : true
15958             });
15959             return this;
15960         },
15961
15962         /**
15963          * Button config that displays a single OK button
15964          * @type Object
15965          */
15966         OK : {ok:true},
15967         /**
15968          * Button config that displays Yes and No buttons
15969          * @type Object
15970          */
15971         YESNO : {yes:true, no:true},
15972         /**
15973          * Button config that displays OK and Cancel buttons
15974          * @type Object
15975          */
15976         OKCANCEL : {ok:true, cancel:true},
15977         /**
15978          * Button config that displays Yes, No and Cancel buttons
15979          * @type Object
15980          */
15981         YESNOCANCEL : {yes:true, no:true, cancel:true},
15982
15983         /**
15984          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15985          * @type Number
15986          */
15987         defaultTextHeight : 75,
15988         /**
15989          * The maximum width in pixels of the message box (defaults to 600)
15990          * @type Number
15991          */
15992         maxWidth : 600,
15993         /**
15994          * The minimum width in pixels of the message box (defaults to 100)
15995          * @type Number
15996          */
15997         minWidth : 100,
15998         /**
15999          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16000          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16001          * @type Number
16002          */
16003         minProgressWidth : 250,
16004         /**
16005          * An object containing the default button text strings that can be overriden for localized language support.
16006          * Supported properties are: ok, cancel, yes and no.
16007          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16008          * @type Object
16009          */
16010         buttonText : {
16011             ok : "OK",
16012             cancel : "Cancel",
16013             yes : "Yes",
16014             no : "No"
16015         }
16016     };
16017 }();
16018
16019 /**
16020  * Shorthand for {@link Roo.MessageBox}
16021  */
16022 Roo.Msg = Roo.MessageBox;/*
16023  * Based on:
16024  * Ext JS Library 1.1.1
16025  * Copyright(c) 2006-2007, Ext JS, LLC.
16026  *
16027  * Originally Released Under LGPL - original licence link has changed is not relivant.
16028  *
16029  * Fork - LGPL
16030  * <script type="text/javascript">
16031  */
16032 /**
16033  * @class Roo.QuickTips
16034  * Provides attractive and customizable tooltips for any element.
16035  * @singleton
16036  */
16037 Roo.QuickTips = function(){
16038     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16039     var ce, bd, xy, dd;
16040     var visible = false, disabled = true, inited = false;
16041     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16042     
16043     var onOver = function(e){
16044         if(disabled){
16045             return;
16046         }
16047         var t = e.getTarget();
16048         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16049             return;
16050         }
16051         if(ce && t == ce.el){
16052             clearTimeout(hideProc);
16053             return;
16054         }
16055         if(t && tagEls[t.id]){
16056             tagEls[t.id].el = t;
16057             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16058             return;
16059         }
16060         var ttp, et = Roo.fly(t);
16061         var ns = cfg.namespace;
16062         if(tm.interceptTitles && t.title){
16063             ttp = t.title;
16064             t.qtip = ttp;
16065             t.removeAttribute("title");
16066             e.preventDefault();
16067         }else{
16068             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16069         }
16070         if(ttp){
16071             showProc = show.defer(tm.showDelay, tm, [{
16072                 el: t, 
16073                 text: ttp, 
16074                 width: et.getAttributeNS(ns, cfg.width),
16075                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16076                 title: et.getAttributeNS(ns, cfg.title),
16077                     cls: et.getAttributeNS(ns, cfg.cls)
16078             }]);
16079         }
16080     };
16081     
16082     var onOut = function(e){
16083         clearTimeout(showProc);
16084         var t = e.getTarget();
16085         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16086             hideProc = setTimeout(hide, tm.hideDelay);
16087         }
16088     };
16089     
16090     var onMove = function(e){
16091         if(disabled){
16092             return;
16093         }
16094         xy = e.getXY();
16095         xy[1] += 18;
16096         if(tm.trackMouse && ce){
16097             el.setXY(xy);
16098         }
16099     };
16100     
16101     var onDown = function(e){
16102         clearTimeout(showProc);
16103         clearTimeout(hideProc);
16104         if(!e.within(el)){
16105             if(tm.hideOnClick){
16106                 hide();
16107                 tm.disable();
16108                 tm.enable.defer(100, tm);
16109             }
16110         }
16111     };
16112     
16113     var getPad = function(){
16114         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16115     };
16116
16117     var show = function(o){
16118         if(disabled){
16119             return;
16120         }
16121         clearTimeout(dismissProc);
16122         ce = o;
16123         if(removeCls){ // in case manually hidden
16124             el.removeClass(removeCls);
16125             removeCls = null;
16126         }
16127         if(ce.cls){
16128             el.addClass(ce.cls);
16129             removeCls = ce.cls;
16130         }
16131         if(ce.title){
16132             tipTitle.update(ce.title);
16133             tipTitle.show();
16134         }else{
16135             tipTitle.update('');
16136             tipTitle.hide();
16137         }
16138         el.dom.style.width  = tm.maxWidth+'px';
16139         //tipBody.dom.style.width = '';
16140         tipBodyText.update(o.text);
16141         var p = getPad(), w = ce.width;
16142         if(!w){
16143             var td = tipBodyText.dom;
16144             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16145             if(aw > tm.maxWidth){
16146                 w = tm.maxWidth;
16147             }else if(aw < tm.minWidth){
16148                 w = tm.minWidth;
16149             }else{
16150                 w = aw;
16151             }
16152         }
16153         //tipBody.setWidth(w);
16154         el.setWidth(parseInt(w, 10) + p);
16155         if(ce.autoHide === false){
16156             close.setDisplayed(true);
16157             if(dd){
16158                 dd.unlock();
16159             }
16160         }else{
16161             close.setDisplayed(false);
16162             if(dd){
16163                 dd.lock();
16164             }
16165         }
16166         if(xy){
16167             el.avoidY = xy[1]-18;
16168             el.setXY(xy);
16169         }
16170         if(tm.animate){
16171             el.setOpacity(.1);
16172             el.setStyle("visibility", "visible");
16173             el.fadeIn({callback: afterShow});
16174         }else{
16175             afterShow();
16176         }
16177     };
16178     
16179     var afterShow = function(){
16180         if(ce){
16181             el.show();
16182             esc.enable();
16183             if(tm.autoDismiss && ce.autoHide !== false){
16184                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16185             }
16186         }
16187     };
16188     
16189     var hide = function(noanim){
16190         clearTimeout(dismissProc);
16191         clearTimeout(hideProc);
16192         ce = null;
16193         if(el.isVisible()){
16194             esc.disable();
16195             if(noanim !== true && tm.animate){
16196                 el.fadeOut({callback: afterHide});
16197             }else{
16198                 afterHide();
16199             } 
16200         }
16201     };
16202     
16203     var afterHide = function(){
16204         el.hide();
16205         if(removeCls){
16206             el.removeClass(removeCls);
16207             removeCls = null;
16208         }
16209     };
16210     
16211     return {
16212         /**
16213         * @cfg {Number} minWidth
16214         * The minimum width of the quick tip (defaults to 40)
16215         */
16216        minWidth : 40,
16217         /**
16218         * @cfg {Number} maxWidth
16219         * The maximum width of the quick tip (defaults to 300)
16220         */
16221        maxWidth : 300,
16222         /**
16223         * @cfg {Boolean} interceptTitles
16224         * True to automatically use the element's DOM title value if available (defaults to false)
16225         */
16226        interceptTitles : false,
16227         /**
16228         * @cfg {Boolean} trackMouse
16229         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16230         */
16231        trackMouse : false,
16232         /**
16233         * @cfg {Boolean} hideOnClick
16234         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16235         */
16236        hideOnClick : true,
16237         /**
16238         * @cfg {Number} showDelay
16239         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16240         */
16241        showDelay : 500,
16242         /**
16243         * @cfg {Number} hideDelay
16244         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16245         */
16246        hideDelay : 200,
16247         /**
16248         * @cfg {Boolean} autoHide
16249         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16250         * Used in conjunction with hideDelay.
16251         */
16252        autoHide : true,
16253         /**
16254         * @cfg {Boolean}
16255         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16256         * (defaults to true).  Used in conjunction with autoDismissDelay.
16257         */
16258        autoDismiss : true,
16259         /**
16260         * @cfg {Number}
16261         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16262         */
16263        autoDismissDelay : 5000,
16264        /**
16265         * @cfg {Boolean} animate
16266         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16267         */
16268        animate : false,
16269
16270        /**
16271         * @cfg {String} title
16272         * Title text to display (defaults to '').  This can be any valid HTML markup.
16273         */
16274         title: '',
16275        /**
16276         * @cfg {String} text
16277         * Body text to display (defaults to '').  This can be any valid HTML markup.
16278         */
16279         text : '',
16280        /**
16281         * @cfg {String} cls
16282         * A CSS class to apply to the base quick tip element (defaults to '').
16283         */
16284         cls : '',
16285        /**
16286         * @cfg {Number} width
16287         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16288         * minWidth or maxWidth.
16289         */
16290         width : null,
16291
16292     /**
16293      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16294      * or display QuickTips in a page.
16295      */
16296        init : function(){
16297           tm = Roo.QuickTips;
16298           cfg = tm.tagConfig;
16299           if(!inited){
16300               if(!Roo.isReady){ // allow calling of init() before onReady
16301                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16302                   return;
16303               }
16304               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16305               el.fxDefaults = {stopFx: true};
16306               // maximum custom styling
16307               //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>');
16308               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>');              
16309               tipTitle = el.child('h3');
16310               tipTitle.enableDisplayMode("block");
16311               tipBody = el.child('div.x-tip-bd');
16312               tipBodyText = el.child('div.x-tip-bd-inner');
16313               //bdLeft = el.child('div.x-tip-bd-left');
16314               //bdRight = el.child('div.x-tip-bd-right');
16315               close = el.child('div.x-tip-close');
16316               close.enableDisplayMode("block");
16317               close.on("click", hide);
16318               var d = Roo.get(document);
16319               d.on("mousedown", onDown);
16320               d.on("mouseover", onOver);
16321               d.on("mouseout", onOut);
16322               d.on("mousemove", onMove);
16323               esc = d.addKeyListener(27, hide);
16324               esc.disable();
16325               if(Roo.dd.DD){
16326                   dd = el.initDD("default", null, {
16327                       onDrag : function(){
16328                           el.sync();  
16329                       }
16330                   });
16331                   dd.setHandleElId(tipTitle.id);
16332                   dd.lock();
16333               }
16334               inited = true;
16335           }
16336           this.enable(); 
16337        },
16338
16339     /**
16340      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16341      * are supported:
16342      * <pre>
16343 Property    Type                   Description
16344 ----------  ---------------------  ------------------------------------------------------------------------
16345 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16346      * </ul>
16347      * @param {Object} config The config object
16348      */
16349        register : function(config){
16350            var cs = config instanceof Array ? config : arguments;
16351            for(var i = 0, len = cs.length; i < len; i++) {
16352                var c = cs[i];
16353                var target = c.target;
16354                if(target){
16355                    if(target instanceof Array){
16356                        for(var j = 0, jlen = target.length; j < jlen; j++){
16357                            tagEls[target[j]] = c;
16358                        }
16359                    }else{
16360                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16361                    }
16362                }
16363            }
16364        },
16365
16366     /**
16367      * Removes this quick tip from its element and destroys it.
16368      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16369      */
16370        unregister : function(el){
16371            delete tagEls[Roo.id(el)];
16372        },
16373
16374     /**
16375      * Enable this quick tip.
16376      */
16377        enable : function(){
16378            if(inited && disabled){
16379                locks.pop();
16380                if(locks.length < 1){
16381                    disabled = false;
16382                }
16383            }
16384        },
16385
16386     /**
16387      * Disable this quick tip.
16388      */
16389        disable : function(){
16390           disabled = true;
16391           clearTimeout(showProc);
16392           clearTimeout(hideProc);
16393           clearTimeout(dismissProc);
16394           if(ce){
16395               hide(true);
16396           }
16397           locks.push(1);
16398        },
16399
16400     /**
16401      * Returns true if the quick tip is enabled, else false.
16402      */
16403        isEnabled : function(){
16404             return !disabled;
16405        },
16406
16407         // private
16408        tagConfig : {
16409            namespace : "ext",
16410            attribute : "qtip",
16411            width : "width",
16412            target : "target",
16413            title : "qtitle",
16414            hide : "hide",
16415            cls : "qclass"
16416        }
16417    };
16418 }();
16419
16420 // backwards compat
16421 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16422  * Based on:
16423  * Ext JS Library 1.1.1
16424  * Copyright(c) 2006-2007, Ext JS, LLC.
16425  *
16426  * Originally Released Under LGPL - original licence link has changed is not relivant.
16427  *
16428  * Fork - LGPL
16429  * <script type="text/javascript">
16430  */
16431  
16432
16433 /**
16434  * @class Roo.tree.TreePanel
16435  * @extends Roo.data.Tree
16436
16437  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16438  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16439  * @cfg {Boolean} enableDD true to enable drag and drop
16440  * @cfg {Boolean} enableDrag true to enable just drag
16441  * @cfg {Boolean} enableDrop true to enable just drop
16442  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16443  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16444  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16445  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16446  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16447  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16448  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16449  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16450  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16451  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16452  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16453  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16454  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16455  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16456  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
16457  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
16458  * 
16459  * @constructor
16460  * @param {String/HTMLElement/Element} el The container element
16461  * @param {Object} config
16462  */
16463 Roo.tree.TreePanel = function(el, config){
16464     var root = false;
16465     var loader = false;
16466     if (config.root) {
16467         root = config.root;
16468         delete config.root;
16469     }
16470     if (config.loader) {
16471         loader = config.loader;
16472         delete config.loader;
16473     }
16474     
16475     Roo.apply(this, config);
16476     Roo.tree.TreePanel.superclass.constructor.call(this);
16477     this.el = Roo.get(el);
16478     this.el.addClass('x-tree');
16479     //console.log(root);
16480     if (root) {
16481         this.setRootNode( Roo.factory(root, Roo.tree));
16482     }
16483     if (loader) {
16484         this.loader = Roo.factory(loader, Roo.tree);
16485     }
16486    /**
16487     * Read-only. The id of the container element becomes this TreePanel's id.
16488     */
16489     this.id = this.el.id;
16490     this.addEvents({
16491         /**
16492         * @event beforeload
16493         * Fires before a node is loaded, return false to cancel
16494         * @param {Node} node The node being loaded
16495         */
16496         "beforeload" : true,
16497         /**
16498         * @event load
16499         * Fires when a node is loaded
16500         * @param {Node} node The node that was loaded
16501         */
16502         "load" : true,
16503         /**
16504         * @event textchange
16505         * Fires when the text for a node is changed
16506         * @param {Node} node The node
16507         * @param {String} text The new text
16508         * @param {String} oldText The old text
16509         */
16510         "textchange" : true,
16511         /**
16512         * @event beforeexpand
16513         * Fires before a node is expanded, return false to cancel.
16514         * @param {Node} node The node
16515         * @param {Boolean} deep
16516         * @param {Boolean} anim
16517         */
16518         "beforeexpand" : true,
16519         /**
16520         * @event beforecollapse
16521         * Fires before a node is collapsed, return false to cancel.
16522         * @param {Node} node The node
16523         * @param {Boolean} deep
16524         * @param {Boolean} anim
16525         */
16526         "beforecollapse" : true,
16527         /**
16528         * @event expand
16529         * Fires when a node is expanded
16530         * @param {Node} node The node
16531         */
16532         "expand" : true,
16533         /**
16534         * @event disabledchange
16535         * Fires when the disabled status of a node changes
16536         * @param {Node} node The node
16537         * @param {Boolean} disabled
16538         */
16539         "disabledchange" : true,
16540         /**
16541         * @event collapse
16542         * Fires when a node is collapsed
16543         * @param {Node} node The node
16544         */
16545         "collapse" : true,
16546         /**
16547         * @event beforeclick
16548         * Fires before click processing on a node. Return false to cancel the default action.
16549         * @param {Node} node The node
16550         * @param {Roo.EventObject} e The event object
16551         */
16552         "beforeclick":true,
16553         /**
16554         * @event checkchange
16555         * Fires when a node with a checkbox's checked property changes
16556         * @param {Node} this This node
16557         * @param {Boolean} checked
16558         */
16559         "checkchange":true,
16560         /**
16561         * @event click
16562         * Fires when a node is clicked
16563         * @param {Node} node The node
16564         * @param {Roo.EventObject} e The event object
16565         */
16566         "click":true,
16567         /**
16568         * @event dblclick
16569         * Fires when a node is double clicked
16570         * @param {Node} node The node
16571         * @param {Roo.EventObject} e The event object
16572         */
16573         "dblclick":true,
16574         /**
16575         * @event contextmenu
16576         * Fires when a node is right clicked
16577         * @param {Node} node The node
16578         * @param {Roo.EventObject} e The event object
16579         */
16580         "contextmenu":true,
16581         /**
16582         * @event beforechildrenrendered
16583         * Fires right before the child nodes for a node are rendered
16584         * @param {Node} node The node
16585         */
16586         "beforechildrenrendered":true,
16587         /**
16588         * @event startdrag
16589         * Fires when a node starts being dragged
16590         * @param {Roo.tree.TreePanel} this
16591         * @param {Roo.tree.TreeNode} node
16592         * @param {event} e The raw browser event
16593         */ 
16594        "startdrag" : true,
16595        /**
16596         * @event enddrag
16597         * Fires when a drag operation is complete
16598         * @param {Roo.tree.TreePanel} this
16599         * @param {Roo.tree.TreeNode} node
16600         * @param {event} e The raw browser event
16601         */
16602        "enddrag" : true,
16603        /**
16604         * @event dragdrop
16605         * Fires when a dragged node is dropped on a valid DD target
16606         * @param {Roo.tree.TreePanel} this
16607         * @param {Roo.tree.TreeNode} node
16608         * @param {DD} dd The dd it was dropped on
16609         * @param {event} e The raw browser event
16610         */
16611        "dragdrop" : true,
16612        /**
16613         * @event beforenodedrop
16614         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16615         * passed to handlers has the following properties:<br />
16616         * <ul style="padding:5px;padding-left:16px;">
16617         * <li>tree - The TreePanel</li>
16618         * <li>target - The node being targeted for the drop</li>
16619         * <li>data - The drag data from the drag source</li>
16620         * <li>point - The point of the drop - append, above or below</li>
16621         * <li>source - The drag source</li>
16622         * <li>rawEvent - Raw mouse event</li>
16623         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16624         * to be inserted by setting them on this object.</li>
16625         * <li>cancel - Set this to true to cancel the drop.</li>
16626         * </ul>
16627         * @param {Object} dropEvent
16628         */
16629        "beforenodedrop" : true,
16630        /**
16631         * @event nodedrop
16632         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16633         * passed to handlers has the following properties:<br />
16634         * <ul style="padding:5px;padding-left:16px;">
16635         * <li>tree - The TreePanel</li>
16636         * <li>target - The node being targeted for the drop</li>
16637         * <li>data - The drag data from the drag source</li>
16638         * <li>point - The point of the drop - append, above or below</li>
16639         * <li>source - The drag source</li>
16640         * <li>rawEvent - Raw mouse event</li>
16641         * <li>dropNode - Dropped node(s).</li>
16642         * </ul>
16643         * @param {Object} dropEvent
16644         */
16645        "nodedrop" : true,
16646         /**
16647         * @event nodedragover
16648         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16649         * passed to handlers has the following properties:<br />
16650         * <ul style="padding:5px;padding-left:16px;">
16651         * <li>tree - The TreePanel</li>
16652         * <li>target - The node being targeted for the drop</li>
16653         * <li>data - The drag data from the drag source</li>
16654         * <li>point - The point of the drop - append, above or below</li>
16655         * <li>source - The drag source</li>
16656         * <li>rawEvent - Raw mouse event</li>
16657         * <li>dropNode - Drop node(s) provided by the source.</li>
16658         * <li>cancel - Set this to true to signal drop not allowed.</li>
16659         * </ul>
16660         * @param {Object} dragOverEvent
16661         */
16662        "nodedragover" : true
16663         
16664     });
16665     if(this.singleExpand){
16666        this.on("beforeexpand", this.restrictExpand, this);
16667     }
16668     if (this.editor) {
16669         this.editor.tree = this;
16670         this.editor = Roo.factory(this.editor, Roo.tree);
16671     }
16672     
16673     if (this.selModel) {
16674         this.selModel = Roo.factory(this.selModel, Roo.tree);
16675     }
16676    
16677 };
16678 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16679     rootVisible : true,
16680     animate: Roo.enableFx,
16681     lines : true,
16682     enableDD : false,
16683     hlDrop : Roo.enableFx,
16684   
16685     renderer: false,
16686     
16687     rendererTip: false,
16688     // private
16689     restrictExpand : function(node){
16690         var p = node.parentNode;
16691         if(p){
16692             if(p.expandedChild && p.expandedChild.parentNode == p){
16693                 p.expandedChild.collapse();
16694             }
16695             p.expandedChild = node;
16696         }
16697     },
16698
16699     // private override
16700     setRootNode : function(node){
16701         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16702         if(!this.rootVisible){
16703             node.ui = new Roo.tree.RootTreeNodeUI(node);
16704         }
16705         return node;
16706     },
16707
16708     /**
16709      * Returns the container element for this TreePanel
16710      */
16711     getEl : function(){
16712         return this.el;
16713     },
16714
16715     /**
16716      * Returns the default TreeLoader for this TreePanel
16717      */
16718     getLoader : function(){
16719         return this.loader;
16720     },
16721
16722     /**
16723      * Expand all nodes
16724      */
16725     expandAll : function(){
16726         this.root.expand(true);
16727     },
16728
16729     /**
16730      * Collapse all nodes
16731      */
16732     collapseAll : function(){
16733         this.root.collapse(true);
16734     },
16735
16736     /**
16737      * Returns the selection model used by this TreePanel
16738      */
16739     getSelectionModel : function(){
16740         if(!this.selModel){
16741             this.selModel = new Roo.tree.DefaultSelectionModel();
16742         }
16743         return this.selModel;
16744     },
16745
16746     /**
16747      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16748      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16749      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16750      * @return {Array}
16751      */
16752     getChecked : function(a, startNode){
16753         startNode = startNode || this.root;
16754         var r = [];
16755         var f = function(){
16756             if(this.attributes.checked){
16757                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16758             }
16759         }
16760         startNode.cascade(f);
16761         return r;
16762     },
16763
16764     /**
16765      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16766      * @param {String} path
16767      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16768      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16769      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16770      */
16771     expandPath : function(path, attr, callback){
16772         attr = attr || "id";
16773         var keys = path.split(this.pathSeparator);
16774         var curNode = this.root;
16775         if(curNode.attributes[attr] != keys[1]){ // invalid root
16776             if(callback){
16777                 callback(false, null);
16778             }
16779             return;
16780         }
16781         var index = 1;
16782         var f = function(){
16783             if(++index == keys.length){
16784                 if(callback){
16785                     callback(true, curNode);
16786                 }
16787                 return;
16788             }
16789             var c = curNode.findChild(attr, keys[index]);
16790             if(!c){
16791                 if(callback){
16792                     callback(false, curNode);
16793                 }
16794                 return;
16795             }
16796             curNode = c;
16797             c.expand(false, false, f);
16798         };
16799         curNode.expand(false, false, f);
16800     },
16801
16802     /**
16803      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16804      * @param {String} path
16805      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16806      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16807      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16808      */
16809     selectPath : function(path, attr, callback){
16810         attr = attr || "id";
16811         var keys = path.split(this.pathSeparator);
16812         var v = keys.pop();
16813         if(keys.length > 0){
16814             var f = function(success, node){
16815                 if(success && node){
16816                     var n = node.findChild(attr, v);
16817                     if(n){
16818                         n.select();
16819                         if(callback){
16820                             callback(true, n);
16821                         }
16822                     }else if(callback){
16823                         callback(false, n);
16824                     }
16825                 }else{
16826                     if(callback){
16827                         callback(false, n);
16828                     }
16829                 }
16830             };
16831             this.expandPath(keys.join(this.pathSeparator), attr, f);
16832         }else{
16833             this.root.select();
16834             if(callback){
16835                 callback(true, this.root);
16836             }
16837         }
16838     },
16839
16840     getTreeEl : function(){
16841         return this.el;
16842     },
16843
16844     /**
16845      * Trigger rendering of this TreePanel
16846      */
16847     render : function(){
16848         if (this.innerCt) {
16849             return this; // stop it rendering more than once!!
16850         }
16851         
16852         this.innerCt = this.el.createChild({tag:"ul",
16853                cls:"x-tree-root-ct " +
16854                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16855
16856         if(this.containerScroll){
16857             Roo.dd.ScrollManager.register(this.el);
16858         }
16859         if((this.enableDD || this.enableDrop) && !this.dropZone){
16860            /**
16861             * The dropZone used by this tree if drop is enabled
16862             * @type Roo.tree.TreeDropZone
16863             */
16864              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16865                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16866            });
16867         }
16868         if((this.enableDD || this.enableDrag) && !this.dragZone){
16869            /**
16870             * The dragZone used by this tree if drag is enabled
16871             * @type Roo.tree.TreeDragZone
16872             */
16873             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16874                ddGroup: this.ddGroup || "TreeDD",
16875                scroll: this.ddScroll
16876            });
16877         }
16878         this.getSelectionModel().init(this);
16879         if (!this.root) {
16880             console.log("ROOT not set in tree");
16881             return;
16882         }
16883         this.root.render();
16884         if(!this.rootVisible){
16885             this.root.renderChildren();
16886         }
16887         return this;
16888     }
16889 });/*
16890  * Based on:
16891  * Ext JS Library 1.1.1
16892  * Copyright(c) 2006-2007, Ext JS, LLC.
16893  *
16894  * Originally Released Under LGPL - original licence link has changed is not relivant.
16895  *
16896  * Fork - LGPL
16897  * <script type="text/javascript">
16898  */
16899  
16900
16901 /**
16902  * @class Roo.tree.DefaultSelectionModel
16903  * @extends Roo.util.Observable
16904  * The default single selection for a TreePanel.
16905  * @param {Object} cfg Configuration
16906  */
16907 Roo.tree.DefaultSelectionModel = function(cfg){
16908    this.selNode = null;
16909    
16910    
16911    
16912    this.addEvents({
16913        /**
16914         * @event selectionchange
16915         * Fires when the selected node changes
16916         * @param {DefaultSelectionModel} this
16917         * @param {TreeNode} node the new selection
16918         */
16919        "selectionchange" : true,
16920
16921        /**
16922         * @event beforeselect
16923         * Fires before the selected node changes, return false to cancel the change
16924         * @param {DefaultSelectionModel} this
16925         * @param {TreeNode} node the new selection
16926         * @param {TreeNode} node the old selection
16927         */
16928        "beforeselect" : true
16929    });
16930    
16931     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16932 };
16933
16934 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16935     init : function(tree){
16936         this.tree = tree;
16937         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16938         tree.on("click", this.onNodeClick, this);
16939     },
16940     
16941     onNodeClick : function(node, e){
16942         if (e.ctrlKey && this.selNode == node)  {
16943             this.unselect(node);
16944             return;
16945         }
16946         this.select(node);
16947     },
16948     
16949     /**
16950      * Select a node.
16951      * @param {TreeNode} node The node to select
16952      * @return {TreeNode} The selected node
16953      */
16954     select : function(node){
16955         var last = this.selNode;
16956         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16957             if(last){
16958                 last.ui.onSelectedChange(false);
16959             }
16960             this.selNode = node;
16961             node.ui.onSelectedChange(true);
16962             this.fireEvent("selectionchange", this, node, last);
16963         }
16964         return node;
16965     },
16966     
16967     /**
16968      * Deselect a node.
16969      * @param {TreeNode} node The node to unselect
16970      */
16971     unselect : function(node){
16972         if(this.selNode == node){
16973             this.clearSelections();
16974         }    
16975     },
16976     
16977     /**
16978      * Clear all selections
16979      */
16980     clearSelections : function(){
16981         var n = this.selNode;
16982         if(n){
16983             n.ui.onSelectedChange(false);
16984             this.selNode = null;
16985             this.fireEvent("selectionchange", this, null);
16986         }
16987         return n;
16988     },
16989     
16990     /**
16991      * Get the selected node
16992      * @return {TreeNode} The selected node
16993      */
16994     getSelectedNode : function(){
16995         return this.selNode;    
16996     },
16997     
16998     /**
16999      * Returns true if the node is selected
17000      * @param {TreeNode} node The node to check
17001      * @return {Boolean}
17002      */
17003     isSelected : function(node){
17004         return this.selNode == node;  
17005     },
17006
17007     /**
17008      * Selects the node above the selected node in the tree, intelligently walking the nodes
17009      * @return TreeNode The new selection
17010      */
17011     selectPrevious : function(){
17012         var s = this.selNode || this.lastSelNode;
17013         if(!s){
17014             return null;
17015         }
17016         var ps = s.previousSibling;
17017         if(ps){
17018             if(!ps.isExpanded() || ps.childNodes.length < 1){
17019                 return this.select(ps);
17020             } else{
17021                 var lc = ps.lastChild;
17022                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17023                     lc = lc.lastChild;
17024                 }
17025                 return this.select(lc);
17026             }
17027         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17028             return this.select(s.parentNode);
17029         }
17030         return null;
17031     },
17032
17033     /**
17034      * Selects the node above the selected node in the tree, intelligently walking the nodes
17035      * @return TreeNode The new selection
17036      */
17037     selectNext : function(){
17038         var s = this.selNode || this.lastSelNode;
17039         if(!s){
17040             return null;
17041         }
17042         if(s.firstChild && s.isExpanded()){
17043              return this.select(s.firstChild);
17044          }else if(s.nextSibling){
17045              return this.select(s.nextSibling);
17046          }else if(s.parentNode){
17047             var newS = null;
17048             s.parentNode.bubble(function(){
17049                 if(this.nextSibling){
17050                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17051                     return false;
17052                 }
17053             });
17054             return newS;
17055          }
17056         return null;
17057     },
17058
17059     onKeyDown : function(e){
17060         var s = this.selNode || this.lastSelNode;
17061         // undesirable, but required
17062         var sm = this;
17063         if(!s){
17064             return;
17065         }
17066         var k = e.getKey();
17067         switch(k){
17068              case e.DOWN:
17069                  e.stopEvent();
17070                  this.selectNext();
17071              break;
17072              case e.UP:
17073                  e.stopEvent();
17074                  this.selectPrevious();
17075              break;
17076              case e.RIGHT:
17077                  e.preventDefault();
17078                  if(s.hasChildNodes()){
17079                      if(!s.isExpanded()){
17080                          s.expand();
17081                      }else if(s.firstChild){
17082                          this.select(s.firstChild, e);
17083                      }
17084                  }
17085              break;
17086              case e.LEFT:
17087                  e.preventDefault();
17088                  if(s.hasChildNodes() && s.isExpanded()){
17089                      s.collapse();
17090                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17091                      this.select(s.parentNode, e);
17092                  }
17093              break;
17094         };
17095     }
17096 });
17097
17098 /**
17099  * @class Roo.tree.MultiSelectionModel
17100  * @extends Roo.util.Observable
17101  * Multi selection for a TreePanel.
17102  * @param {Object} cfg Configuration
17103  */
17104 Roo.tree.MultiSelectionModel = function(){
17105    this.selNodes = [];
17106    this.selMap = {};
17107    this.addEvents({
17108        /**
17109         * @event selectionchange
17110         * Fires when the selected nodes change
17111         * @param {MultiSelectionModel} this
17112         * @param {Array} nodes Array of the selected nodes
17113         */
17114        "selectionchange" : true
17115    });
17116    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17117    
17118 };
17119
17120 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17121     init : function(tree){
17122         this.tree = tree;
17123         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17124         tree.on("click", this.onNodeClick, this);
17125     },
17126     
17127     onNodeClick : function(node, e){
17128         this.select(node, e, e.ctrlKey);
17129     },
17130     
17131     /**
17132      * Select a node.
17133      * @param {TreeNode} node The node to select
17134      * @param {EventObject} e (optional) An event associated with the selection
17135      * @param {Boolean} keepExisting True to retain existing selections
17136      * @return {TreeNode} The selected node
17137      */
17138     select : function(node, e, keepExisting){
17139         if(keepExisting !== true){
17140             this.clearSelections(true);
17141         }
17142         if(this.isSelected(node)){
17143             this.lastSelNode = node;
17144             return node;
17145         }
17146         this.selNodes.push(node);
17147         this.selMap[node.id] = node;
17148         this.lastSelNode = node;
17149         node.ui.onSelectedChange(true);
17150         this.fireEvent("selectionchange", this, this.selNodes);
17151         return node;
17152     },
17153     
17154     /**
17155      * Deselect a node.
17156      * @param {TreeNode} node The node to unselect
17157      */
17158     unselect : function(node){
17159         if(this.selMap[node.id]){
17160             node.ui.onSelectedChange(false);
17161             var sn = this.selNodes;
17162             var index = -1;
17163             if(sn.indexOf){
17164                 index = sn.indexOf(node);
17165             }else{
17166                 for(var i = 0, len = sn.length; i < len; i++){
17167                     if(sn[i] == node){
17168                         index = i;
17169                         break;
17170                     }
17171                 }
17172             }
17173             if(index != -1){
17174                 this.selNodes.splice(index, 1);
17175             }
17176             delete this.selMap[node.id];
17177             this.fireEvent("selectionchange", this, this.selNodes);
17178         }
17179     },
17180     
17181     /**
17182      * Clear all selections
17183      */
17184     clearSelections : function(suppressEvent){
17185         var sn = this.selNodes;
17186         if(sn.length > 0){
17187             for(var i = 0, len = sn.length; i < len; i++){
17188                 sn[i].ui.onSelectedChange(false);
17189             }
17190             this.selNodes = [];
17191             this.selMap = {};
17192             if(suppressEvent !== true){
17193                 this.fireEvent("selectionchange", this, this.selNodes);
17194             }
17195         }
17196     },
17197     
17198     /**
17199      * Returns true if the node is selected
17200      * @param {TreeNode} node The node to check
17201      * @return {Boolean}
17202      */
17203     isSelected : function(node){
17204         return this.selMap[node.id] ? true : false;  
17205     },
17206     
17207     /**
17208      * Returns an array of the selected nodes
17209      * @return {Array}
17210      */
17211     getSelectedNodes : function(){
17212         return this.selNodes;    
17213     },
17214
17215     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17216
17217     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17218
17219     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17220 });/*
17221  * Based on:
17222  * Ext JS Library 1.1.1
17223  * Copyright(c) 2006-2007, Ext JS, LLC.
17224  *
17225  * Originally Released Under LGPL - original licence link has changed is not relivant.
17226  *
17227  * Fork - LGPL
17228  * <script type="text/javascript">
17229  */
17230  
17231 /**
17232  * @class Roo.tree.TreeNode
17233  * @extends Roo.data.Node
17234  * @cfg {String} text The text for this node
17235  * @cfg {Boolean} expanded true to start the node expanded
17236  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17237  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17238  * @cfg {Boolean} disabled true to start the node disabled
17239  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17240  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17241  * @cfg {String} cls A css class to be added to the node
17242  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17243  * @cfg {String} href URL of the link used for the node (defaults to #)
17244  * @cfg {String} hrefTarget target frame for the link
17245  * @cfg {String} qtip An Ext QuickTip for the node
17246  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17247  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17248  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17249  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17250  * (defaults to undefined with no checkbox rendered)
17251  * @constructor
17252  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17253  */
17254 Roo.tree.TreeNode = function(attributes){
17255     attributes = attributes || {};
17256     if(typeof attributes == "string"){
17257         attributes = {text: attributes};
17258     }
17259     this.childrenRendered = false;
17260     this.rendered = false;
17261     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17262     this.expanded = attributes.expanded === true;
17263     this.isTarget = attributes.isTarget !== false;
17264     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17265     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17266
17267     /**
17268      * Read-only. The text for this node. To change it use setText().
17269      * @type String
17270      */
17271     this.text = attributes.text;
17272     /**
17273      * True if this node is disabled.
17274      * @type Boolean
17275      */
17276     this.disabled = attributes.disabled === true;
17277
17278     this.addEvents({
17279         /**
17280         * @event textchange
17281         * Fires when the text for this node is changed
17282         * @param {Node} this This node
17283         * @param {String} text The new text
17284         * @param {String} oldText The old text
17285         */
17286         "textchange" : true,
17287         /**
17288         * @event beforeexpand
17289         * Fires before this node is expanded, return false to cancel.
17290         * @param {Node} this This node
17291         * @param {Boolean} deep
17292         * @param {Boolean} anim
17293         */
17294         "beforeexpand" : true,
17295         /**
17296         * @event beforecollapse
17297         * Fires before this node is collapsed, return false to cancel.
17298         * @param {Node} this This node
17299         * @param {Boolean} deep
17300         * @param {Boolean} anim
17301         */
17302         "beforecollapse" : true,
17303         /**
17304         * @event expand
17305         * Fires when this node is expanded
17306         * @param {Node} this This node
17307         */
17308         "expand" : true,
17309         /**
17310         * @event disabledchange
17311         * Fires when the disabled status of this node changes
17312         * @param {Node} this This node
17313         * @param {Boolean} disabled
17314         */
17315         "disabledchange" : true,
17316         /**
17317         * @event collapse
17318         * Fires when this node is collapsed
17319         * @param {Node} this This node
17320         */
17321         "collapse" : true,
17322         /**
17323         * @event beforeclick
17324         * Fires before click processing. Return false to cancel the default action.
17325         * @param {Node} this This node
17326         * @param {Roo.EventObject} e The event object
17327         */
17328         "beforeclick":true,
17329         /**
17330         * @event checkchange
17331         * Fires when a node with a checkbox's checked property changes
17332         * @param {Node} this This node
17333         * @param {Boolean} checked
17334         */
17335         "checkchange":true,
17336         /**
17337         * @event click
17338         * Fires when this node is clicked
17339         * @param {Node} this This node
17340         * @param {Roo.EventObject} e The event object
17341         */
17342         "click":true,
17343         /**
17344         * @event dblclick
17345         * Fires when this node is double clicked
17346         * @param {Node} this This node
17347         * @param {Roo.EventObject} e The event object
17348         */
17349         "dblclick":true,
17350         /**
17351         * @event contextmenu
17352         * Fires when this node is right clicked
17353         * @param {Node} this This node
17354         * @param {Roo.EventObject} e The event object
17355         */
17356         "contextmenu":true,
17357         /**
17358         * @event beforechildrenrendered
17359         * Fires right before the child nodes for this node are rendered
17360         * @param {Node} this This node
17361         */
17362         "beforechildrenrendered":true
17363     });
17364
17365     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17366
17367     /**
17368      * Read-only. The UI for this node
17369      * @type TreeNodeUI
17370      */
17371     this.ui = new uiClass(this);
17372 };
17373 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17374     preventHScroll: true,
17375     /**
17376      * Returns true if this node is expanded
17377      * @return {Boolean}
17378      */
17379     isExpanded : function(){
17380         return this.expanded;
17381     },
17382
17383     /**
17384      * Returns the UI object for this node
17385      * @return {TreeNodeUI}
17386      */
17387     getUI : function(){
17388         return this.ui;
17389     },
17390
17391     // private override
17392     setFirstChild : function(node){
17393         var of = this.firstChild;
17394         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17395         if(this.childrenRendered && of && node != of){
17396             of.renderIndent(true, true);
17397         }
17398         if(this.rendered){
17399             this.renderIndent(true, true);
17400         }
17401     },
17402
17403     // private override
17404     setLastChild : function(node){
17405         var ol = this.lastChild;
17406         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17407         if(this.childrenRendered && ol && node != ol){
17408             ol.renderIndent(true, true);
17409         }
17410         if(this.rendered){
17411             this.renderIndent(true, true);
17412         }
17413     },
17414
17415     // these methods are overridden to provide lazy rendering support
17416     // private override
17417     appendChild : function(){
17418         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17419         if(node && this.childrenRendered){
17420             node.render();
17421         }
17422         this.ui.updateExpandIcon();
17423         return node;
17424     },
17425
17426     // private override
17427     removeChild : function(node){
17428         this.ownerTree.getSelectionModel().unselect(node);
17429         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17430         // if it's been rendered remove dom node
17431         if(this.childrenRendered){
17432             node.ui.remove();
17433         }
17434         if(this.childNodes.length < 1){
17435             this.collapse(false, false);
17436         }else{
17437             this.ui.updateExpandIcon();
17438         }
17439         if(!this.firstChild) {
17440             this.childrenRendered = false;
17441         }
17442         return node;
17443     },
17444
17445     // private override
17446     insertBefore : function(node, refNode){
17447         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17448         if(newNode && refNode && this.childrenRendered){
17449             node.render();
17450         }
17451         this.ui.updateExpandIcon();
17452         return newNode;
17453     },
17454
17455     /**
17456      * Sets the text for this node
17457      * @param {String} text
17458      */
17459     setText : function(text){
17460         var oldText = this.text;
17461         this.text = text;
17462         this.attributes.text = text;
17463         if(this.rendered){ // event without subscribing
17464             this.ui.onTextChange(this, text, oldText);
17465         }
17466         this.fireEvent("textchange", this, text, oldText);
17467     },
17468
17469     /**
17470      * Triggers selection of this node
17471      */
17472     select : function(){
17473         this.getOwnerTree().getSelectionModel().select(this);
17474     },
17475
17476     /**
17477      * Triggers deselection of this node
17478      */
17479     unselect : function(){
17480         this.getOwnerTree().getSelectionModel().unselect(this);
17481     },
17482
17483     /**
17484      * Returns true if this node is selected
17485      * @return {Boolean}
17486      */
17487     isSelected : function(){
17488         return this.getOwnerTree().getSelectionModel().isSelected(this);
17489     },
17490
17491     /**
17492      * Expand this node.
17493      * @param {Boolean} deep (optional) True to expand all children as well
17494      * @param {Boolean} anim (optional) false to cancel the default animation
17495      * @param {Function} callback (optional) A callback to be called when
17496      * expanding this node completes (does not wait for deep expand to complete).
17497      * Called with 1 parameter, this node.
17498      */
17499     expand : function(deep, anim, callback){
17500         if(!this.expanded){
17501             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17502                 return;
17503             }
17504             if(!this.childrenRendered){
17505                 this.renderChildren();
17506             }
17507             this.expanded = true;
17508             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17509                 this.ui.animExpand(function(){
17510                     this.fireEvent("expand", this);
17511                     if(typeof callback == "function"){
17512                         callback(this);
17513                     }
17514                     if(deep === true){
17515                         this.expandChildNodes(true);
17516                     }
17517                 }.createDelegate(this));
17518                 return;
17519             }else{
17520                 this.ui.expand();
17521                 this.fireEvent("expand", this);
17522                 if(typeof callback == "function"){
17523                     callback(this);
17524                 }
17525             }
17526         }else{
17527            if(typeof callback == "function"){
17528                callback(this);
17529            }
17530         }
17531         if(deep === true){
17532             this.expandChildNodes(true);
17533         }
17534     },
17535
17536     isHiddenRoot : function(){
17537         return this.isRoot && !this.getOwnerTree().rootVisible;
17538     },
17539
17540     /**
17541      * Collapse this node.
17542      * @param {Boolean} deep (optional) True to collapse all children as well
17543      * @param {Boolean} anim (optional) false to cancel the default animation
17544      */
17545     collapse : function(deep, anim){
17546         if(this.expanded && !this.isHiddenRoot()){
17547             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17548                 return;
17549             }
17550             this.expanded = false;
17551             if((this.getOwnerTree().animate && anim !== false) || anim){
17552                 this.ui.animCollapse(function(){
17553                     this.fireEvent("collapse", this);
17554                     if(deep === true){
17555                         this.collapseChildNodes(true);
17556                     }
17557                 }.createDelegate(this));
17558                 return;
17559             }else{
17560                 this.ui.collapse();
17561                 this.fireEvent("collapse", this);
17562             }
17563         }
17564         if(deep === true){
17565             var cs = this.childNodes;
17566             for(var i = 0, len = cs.length; i < len; i++) {
17567                 cs[i].collapse(true, false);
17568             }
17569         }
17570     },
17571
17572     // private
17573     delayedExpand : function(delay){
17574         if(!this.expandProcId){
17575             this.expandProcId = this.expand.defer(delay, this);
17576         }
17577     },
17578
17579     // private
17580     cancelExpand : function(){
17581         if(this.expandProcId){
17582             clearTimeout(this.expandProcId);
17583         }
17584         this.expandProcId = false;
17585     },
17586
17587     /**
17588      * Toggles expanded/collapsed state of the node
17589      */
17590     toggle : function(){
17591         if(this.expanded){
17592             this.collapse();
17593         }else{
17594             this.expand();
17595         }
17596     },
17597
17598     /**
17599      * Ensures all parent nodes are expanded
17600      */
17601     ensureVisible : function(callback){
17602         var tree = this.getOwnerTree();
17603         tree.expandPath(this.parentNode.getPath(), false, function(){
17604             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17605             Roo.callback(callback);
17606         }.createDelegate(this));
17607     },
17608
17609     /**
17610      * Expand all child nodes
17611      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17612      */
17613     expandChildNodes : function(deep){
17614         var cs = this.childNodes;
17615         for(var i = 0, len = cs.length; i < len; i++) {
17616                 cs[i].expand(deep);
17617         }
17618     },
17619
17620     /**
17621      * Collapse all child nodes
17622      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17623      */
17624     collapseChildNodes : function(deep){
17625         var cs = this.childNodes;
17626         for(var i = 0, len = cs.length; i < len; i++) {
17627                 cs[i].collapse(deep);
17628         }
17629     },
17630
17631     /**
17632      * Disables this node
17633      */
17634     disable : function(){
17635         this.disabled = true;
17636         this.unselect();
17637         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17638             this.ui.onDisableChange(this, true);
17639         }
17640         this.fireEvent("disabledchange", this, true);
17641     },
17642
17643     /**
17644      * Enables this node
17645      */
17646     enable : function(){
17647         this.disabled = false;
17648         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17649             this.ui.onDisableChange(this, false);
17650         }
17651         this.fireEvent("disabledchange", this, false);
17652     },
17653
17654     // private
17655     renderChildren : function(suppressEvent){
17656         if(suppressEvent !== false){
17657             this.fireEvent("beforechildrenrendered", this);
17658         }
17659         var cs = this.childNodes;
17660         for(var i = 0, len = cs.length; i < len; i++){
17661             cs[i].render(true);
17662         }
17663         this.childrenRendered = true;
17664     },
17665
17666     // private
17667     sort : function(fn, scope){
17668         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17669         if(this.childrenRendered){
17670             var cs = this.childNodes;
17671             for(var i = 0, len = cs.length; i < len; i++){
17672                 cs[i].render(true);
17673             }
17674         }
17675     },
17676
17677     // private
17678     render : function(bulkRender){
17679         this.ui.render(bulkRender);
17680         if(!this.rendered){
17681             this.rendered = true;
17682             if(this.expanded){
17683                 this.expanded = false;
17684                 this.expand(false, false);
17685             }
17686         }
17687     },
17688
17689     // private
17690     renderIndent : function(deep, refresh){
17691         if(refresh){
17692             this.ui.childIndent = null;
17693         }
17694         this.ui.renderIndent();
17695         if(deep === true && this.childrenRendered){
17696             var cs = this.childNodes;
17697             for(var i = 0, len = cs.length; i < len; i++){
17698                 cs[i].renderIndent(true, refresh);
17699             }
17700         }
17701     }
17702 });/*
17703  * Based on:
17704  * Ext JS Library 1.1.1
17705  * Copyright(c) 2006-2007, Ext JS, LLC.
17706  *
17707  * Originally Released Under LGPL - original licence link has changed is not relivant.
17708  *
17709  * Fork - LGPL
17710  * <script type="text/javascript">
17711  */
17712  
17713 /**
17714  * @class Roo.tree.AsyncTreeNode
17715  * @extends Roo.tree.TreeNode
17716  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17717  * @constructor
17718  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17719  */
17720  Roo.tree.AsyncTreeNode = function(config){
17721     this.loaded = false;
17722     this.loading = false;
17723     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17724     /**
17725     * @event beforeload
17726     * Fires before this node is loaded, return false to cancel
17727     * @param {Node} this This node
17728     */
17729     this.addEvents({'beforeload':true, 'load': true});
17730     /**
17731     * @event load
17732     * Fires when this node is loaded
17733     * @param {Node} this This node
17734     */
17735     /**
17736      * The loader used by this node (defaults to using the tree's defined loader)
17737      * @type TreeLoader
17738      * @property loader
17739      */
17740 };
17741 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17742     expand : function(deep, anim, callback){
17743         if(this.loading){ // if an async load is already running, waiting til it's done
17744             var timer;
17745             var f = function(){
17746                 if(!this.loading){ // done loading
17747                     clearInterval(timer);
17748                     this.expand(deep, anim, callback);
17749                 }
17750             }.createDelegate(this);
17751             timer = setInterval(f, 200);
17752             return;
17753         }
17754         if(!this.loaded){
17755             if(this.fireEvent("beforeload", this) === false){
17756                 return;
17757             }
17758             this.loading = true;
17759             this.ui.beforeLoad(this);
17760             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17761             if(loader){
17762                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17763                 return;
17764             }
17765         }
17766         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17767     },
17768     
17769     /**
17770      * Returns true if this node is currently loading
17771      * @return {Boolean}
17772      */
17773     isLoading : function(){
17774         return this.loading;  
17775     },
17776     
17777     loadComplete : function(deep, anim, callback){
17778         this.loading = false;
17779         this.loaded = true;
17780         this.ui.afterLoad(this);
17781         this.fireEvent("load", this);
17782         this.expand(deep, anim, callback);
17783     },
17784     
17785     /**
17786      * Returns true if this node has been loaded
17787      * @return {Boolean}
17788      */
17789     isLoaded : function(){
17790         return this.loaded;
17791     },
17792     
17793     hasChildNodes : function(){
17794         if(!this.isLeaf() && !this.loaded){
17795             return true;
17796         }else{
17797             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17798         }
17799     },
17800
17801     /**
17802      * Trigger a reload for this node
17803      * @param {Function} callback
17804      */
17805     reload : function(callback){
17806         this.collapse(false, false);
17807         while(this.firstChild){
17808             this.removeChild(this.firstChild);
17809         }
17810         this.childrenRendered = false;
17811         this.loaded = false;
17812         if(this.isHiddenRoot()){
17813             this.expanded = false;
17814         }
17815         this.expand(false, false, callback);
17816     }
17817 });/*
17818  * Based on:
17819  * Ext JS Library 1.1.1
17820  * Copyright(c) 2006-2007, Ext JS, LLC.
17821  *
17822  * Originally Released Under LGPL - original licence link has changed is not relivant.
17823  *
17824  * Fork - LGPL
17825  * <script type="text/javascript">
17826  */
17827  
17828 /**
17829  * @class Roo.tree.TreeNodeUI
17830  * @constructor
17831  * @param {Object} node The node to render
17832  * The TreeNode UI implementation is separate from the
17833  * tree implementation. Unless you are customizing the tree UI,
17834  * you should never have to use this directly.
17835  */
17836 Roo.tree.TreeNodeUI = function(node){
17837     this.node = node;
17838     this.rendered = false;
17839     this.animating = false;
17840     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17841 };
17842
17843 Roo.tree.TreeNodeUI.prototype = {
17844     removeChild : function(node){
17845         if(this.rendered){
17846             this.ctNode.removeChild(node.ui.getEl());
17847         }
17848     },
17849
17850     beforeLoad : function(){
17851          this.addClass("x-tree-node-loading");
17852     },
17853
17854     afterLoad : function(){
17855          this.removeClass("x-tree-node-loading");
17856     },
17857
17858     onTextChange : function(node, text, oldText){
17859         if(this.rendered){
17860             this.textNode.innerHTML = text;
17861         }
17862     },
17863
17864     onDisableChange : function(node, state){
17865         this.disabled = state;
17866         if(state){
17867             this.addClass("x-tree-node-disabled");
17868         }else{
17869             this.removeClass("x-tree-node-disabled");
17870         }
17871     },
17872
17873     onSelectedChange : function(state){
17874         if(state){
17875             this.focus();
17876             this.addClass("x-tree-selected");
17877         }else{
17878             //this.blur();
17879             this.removeClass("x-tree-selected");
17880         }
17881     },
17882
17883     onMove : function(tree, node, oldParent, newParent, index, refNode){
17884         this.childIndent = null;
17885         if(this.rendered){
17886             var targetNode = newParent.ui.getContainer();
17887             if(!targetNode){//target not rendered
17888                 this.holder = document.createElement("div");
17889                 this.holder.appendChild(this.wrap);
17890                 return;
17891             }
17892             var insertBefore = refNode ? refNode.ui.getEl() : null;
17893             if(insertBefore){
17894                 targetNode.insertBefore(this.wrap, insertBefore);
17895             }else{
17896                 targetNode.appendChild(this.wrap);
17897             }
17898             this.node.renderIndent(true);
17899         }
17900     },
17901
17902     addClass : function(cls){
17903         if(this.elNode){
17904             Roo.fly(this.elNode).addClass(cls);
17905         }
17906     },
17907
17908     removeClass : function(cls){
17909         if(this.elNode){
17910             Roo.fly(this.elNode).removeClass(cls);
17911         }
17912     },
17913
17914     remove : function(){
17915         if(this.rendered){
17916             this.holder = document.createElement("div");
17917             this.holder.appendChild(this.wrap);
17918         }
17919     },
17920
17921     fireEvent : function(){
17922         return this.node.fireEvent.apply(this.node, arguments);
17923     },
17924
17925     initEvents : function(){
17926         this.node.on("move", this.onMove, this);
17927         var E = Roo.EventManager;
17928         var a = this.anchor;
17929
17930         var el = Roo.fly(a, '_treeui');
17931
17932         if(Roo.isOpera){ // opera render bug ignores the CSS
17933             el.setStyle("text-decoration", "none");
17934         }
17935
17936         el.on("click", this.onClick, this);
17937         el.on("dblclick", this.onDblClick, this);
17938
17939         if(this.checkbox){
17940             Roo.EventManager.on(this.checkbox,
17941                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17942         }
17943
17944         el.on("contextmenu", this.onContextMenu, this);
17945
17946         var icon = Roo.fly(this.iconNode);
17947         icon.on("click", this.onClick, this);
17948         icon.on("dblclick", this.onDblClick, this);
17949         icon.on("contextmenu", this.onContextMenu, this);
17950         E.on(this.ecNode, "click", this.ecClick, this, true);
17951
17952         if(this.node.disabled){
17953             this.addClass("x-tree-node-disabled");
17954         }
17955         if(this.node.hidden){
17956             this.addClass("x-tree-node-disabled");
17957         }
17958         var ot = this.node.getOwnerTree();
17959         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17960         if(dd && (!this.node.isRoot || ot.rootVisible)){
17961             Roo.dd.Registry.register(this.elNode, {
17962                 node: this.node,
17963                 handles: this.getDDHandles(),
17964                 isHandle: false
17965             });
17966         }
17967     },
17968
17969     getDDHandles : function(){
17970         return [this.iconNode, this.textNode];
17971     },
17972
17973     hide : function(){
17974         if(this.rendered){
17975             this.wrap.style.display = "none";
17976         }
17977     },
17978
17979     show : function(){
17980         if(this.rendered){
17981             this.wrap.style.display = "";
17982         }
17983     },
17984
17985     onContextMenu : function(e){
17986         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17987             e.preventDefault();
17988             this.focus();
17989             this.fireEvent("contextmenu", this.node, e);
17990         }
17991     },
17992
17993     onClick : function(e){
17994         if(this.dropping){
17995             e.stopEvent();
17996             return;
17997         }
17998         if(this.fireEvent("beforeclick", this.node, e) !== false){
17999             if(!this.disabled && this.node.attributes.href){
18000                 this.fireEvent("click", this.node, e);
18001                 return;
18002             }
18003             e.preventDefault();
18004             if(this.disabled){
18005                 return;
18006             }
18007
18008             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18009                 this.node.toggle();
18010             }
18011
18012             this.fireEvent("click", this.node, e);
18013         }else{
18014             e.stopEvent();
18015         }
18016     },
18017
18018     onDblClick : function(e){
18019         e.preventDefault();
18020         if(this.disabled){
18021             return;
18022         }
18023         if(this.checkbox){
18024             this.toggleCheck();
18025         }
18026         if(!this.animating && this.node.hasChildNodes()){
18027             this.node.toggle();
18028         }
18029         this.fireEvent("dblclick", this.node, e);
18030     },
18031
18032     onCheckChange : function(){
18033         var checked = this.checkbox.checked;
18034         this.node.attributes.checked = checked;
18035         this.fireEvent('checkchange', this.node, checked);
18036     },
18037
18038     ecClick : function(e){
18039         if(!this.animating && this.node.hasChildNodes()){
18040             this.node.toggle();
18041         }
18042     },
18043
18044     startDrop : function(){
18045         this.dropping = true;
18046     },
18047
18048     // delayed drop so the click event doesn't get fired on a drop
18049     endDrop : function(){
18050        setTimeout(function(){
18051            this.dropping = false;
18052        }.createDelegate(this), 50);
18053     },
18054
18055     expand : function(){
18056         this.updateExpandIcon();
18057         this.ctNode.style.display = "";
18058     },
18059
18060     focus : function(){
18061         if(!this.node.preventHScroll){
18062             try{this.anchor.focus();
18063             }catch(e){}
18064         }else if(!Roo.isIE){
18065             try{
18066                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18067                 var l = noscroll.scrollLeft;
18068                 this.anchor.focus();
18069                 noscroll.scrollLeft = l;
18070             }catch(e){}
18071         }
18072     },
18073
18074     toggleCheck : function(value){
18075         var cb = this.checkbox;
18076         if(cb){
18077             cb.checked = (value === undefined ? !cb.checked : value);
18078         }
18079     },
18080
18081     blur : function(){
18082         try{
18083             this.anchor.blur();
18084         }catch(e){}
18085     },
18086
18087     animExpand : function(callback){
18088         var ct = Roo.get(this.ctNode);
18089         ct.stopFx();
18090         if(!this.node.hasChildNodes()){
18091             this.updateExpandIcon();
18092             this.ctNode.style.display = "";
18093             Roo.callback(callback);
18094             return;
18095         }
18096         this.animating = true;
18097         this.updateExpandIcon();
18098
18099         ct.slideIn('t', {
18100            callback : function(){
18101                this.animating = false;
18102                Roo.callback(callback);
18103             },
18104             scope: this,
18105             duration: this.node.ownerTree.duration || .25
18106         });
18107     },
18108
18109     highlight : function(){
18110         var tree = this.node.getOwnerTree();
18111         Roo.fly(this.wrap).highlight(
18112             tree.hlColor || "C3DAF9",
18113             {endColor: tree.hlBaseColor}
18114         );
18115     },
18116
18117     collapse : function(){
18118         this.updateExpandIcon();
18119         this.ctNode.style.display = "none";
18120     },
18121
18122     animCollapse : function(callback){
18123         var ct = Roo.get(this.ctNode);
18124         ct.enableDisplayMode('block');
18125         ct.stopFx();
18126
18127         this.animating = true;
18128         this.updateExpandIcon();
18129
18130         ct.slideOut('t', {
18131             callback : function(){
18132                this.animating = false;
18133                Roo.callback(callback);
18134             },
18135             scope: this,
18136             duration: this.node.ownerTree.duration || .25
18137         });
18138     },
18139
18140     getContainer : function(){
18141         return this.ctNode;
18142     },
18143
18144     getEl : function(){
18145         return this.wrap;
18146     },
18147
18148     appendDDGhost : function(ghostNode){
18149         ghostNode.appendChild(this.elNode.cloneNode(true));
18150     },
18151
18152     getDDRepairXY : function(){
18153         return Roo.lib.Dom.getXY(this.iconNode);
18154     },
18155
18156     onRender : function(){
18157         this.render();
18158     },
18159
18160     render : function(bulkRender){
18161         var n = this.node, a = n.attributes;
18162         var targetNode = n.parentNode ?
18163               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18164
18165         if(!this.rendered){
18166             this.rendered = true;
18167
18168             this.renderElements(n, a, targetNode, bulkRender);
18169
18170             if(a.qtip){
18171                if(this.textNode.setAttributeNS){
18172                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18173                    if(a.qtipTitle){
18174                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18175                    }
18176                }else{
18177                    this.textNode.setAttribute("ext:qtip", a.qtip);
18178                    if(a.qtipTitle){
18179                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18180                    }
18181                }
18182             }else if(a.qtipCfg){
18183                 a.qtipCfg.target = Roo.id(this.textNode);
18184                 Roo.QuickTips.register(a.qtipCfg);
18185             }
18186             this.initEvents();
18187             if(!this.node.expanded){
18188                 this.updateExpandIcon();
18189             }
18190         }else{
18191             if(bulkRender === true) {
18192                 targetNode.appendChild(this.wrap);
18193             }
18194         }
18195     },
18196
18197     renderElements : function(n, a, targetNode, bulkRender)
18198     {
18199         // add some indent caching, this helps performance when rendering a large tree
18200         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18201         var t = n.getOwnerTree();
18202         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18203         if (typeof(n.attributes.html) != 'undefined') {
18204             txt = n.attributes.html;
18205         }
18206         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18207         var cb = typeof a.checked == 'boolean';
18208         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18209         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18210             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18211             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18212             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18213             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18214             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18215              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18216                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18217             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18218             "</li>"];
18219
18220         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18221             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18222                                 n.nextSibling.ui.getEl(), buf.join(""));
18223         }else{
18224             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18225         }
18226
18227         this.elNode = this.wrap.childNodes[0];
18228         this.ctNode = this.wrap.childNodes[1];
18229         var cs = this.elNode.childNodes;
18230         this.indentNode = cs[0];
18231         this.ecNode = cs[1];
18232         this.iconNode = cs[2];
18233         var index = 3;
18234         if(cb){
18235             this.checkbox = cs[3];
18236             index++;
18237         }
18238         this.anchor = cs[index];
18239         this.textNode = cs[index].firstChild;
18240     },
18241
18242     getAnchor : function(){
18243         return this.anchor;
18244     },
18245
18246     getTextEl : function(){
18247         return this.textNode;
18248     },
18249
18250     getIconEl : function(){
18251         return this.iconNode;
18252     },
18253
18254     isChecked : function(){
18255         return this.checkbox ? this.checkbox.checked : false;
18256     },
18257
18258     updateExpandIcon : function(){
18259         if(this.rendered){
18260             var n = this.node, c1, c2;
18261             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18262             var hasChild = n.hasChildNodes();
18263             if(hasChild){
18264                 if(n.expanded){
18265                     cls += "-minus";
18266                     c1 = "x-tree-node-collapsed";
18267                     c2 = "x-tree-node-expanded";
18268                 }else{
18269                     cls += "-plus";
18270                     c1 = "x-tree-node-expanded";
18271                     c2 = "x-tree-node-collapsed";
18272                 }
18273                 if(this.wasLeaf){
18274                     this.removeClass("x-tree-node-leaf");
18275                     this.wasLeaf = false;
18276                 }
18277                 if(this.c1 != c1 || this.c2 != c2){
18278                     Roo.fly(this.elNode).replaceClass(c1, c2);
18279                     this.c1 = c1; this.c2 = c2;
18280                 }
18281             }else{
18282                 // this changes non-leafs into leafs if they have no children.
18283                 // it's not very rational behaviour..
18284                 
18285                 if(!this.wasLeaf && this.node.leaf){
18286                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18287                     delete this.c1;
18288                     delete this.c2;
18289                     this.wasLeaf = true;
18290                 }
18291             }
18292             var ecc = "x-tree-ec-icon "+cls;
18293             if(this.ecc != ecc){
18294                 this.ecNode.className = ecc;
18295                 this.ecc = ecc;
18296             }
18297         }
18298     },
18299
18300     getChildIndent : function(){
18301         if(!this.childIndent){
18302             var buf = [];
18303             var p = this.node;
18304             while(p){
18305                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18306                     if(!p.isLast()) {
18307                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18308                     } else {
18309                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18310                     }
18311                 }
18312                 p = p.parentNode;
18313             }
18314             this.childIndent = buf.join("");
18315         }
18316         return this.childIndent;
18317     },
18318
18319     renderIndent : function(){
18320         if(this.rendered){
18321             var indent = "";
18322             var p = this.node.parentNode;
18323             if(p){
18324                 indent = p.ui.getChildIndent();
18325             }
18326             if(this.indentMarkup != indent){ // don't rerender if not required
18327                 this.indentNode.innerHTML = indent;
18328                 this.indentMarkup = indent;
18329             }
18330             this.updateExpandIcon();
18331         }
18332     }
18333 };
18334
18335 Roo.tree.RootTreeNodeUI = function(){
18336     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18337 };
18338 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18339     render : function(){
18340         if(!this.rendered){
18341             var targetNode = this.node.ownerTree.innerCt.dom;
18342             this.node.expanded = true;
18343             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18344             this.wrap = this.ctNode = targetNode.firstChild;
18345         }
18346     },
18347     collapse : function(){
18348     },
18349     expand : function(){
18350     }
18351 });/*
18352  * Based on:
18353  * Ext JS Library 1.1.1
18354  * Copyright(c) 2006-2007, Ext JS, LLC.
18355  *
18356  * Originally Released Under LGPL - original licence link has changed is not relivant.
18357  *
18358  * Fork - LGPL
18359  * <script type="text/javascript">
18360  */
18361 /**
18362  * @class Roo.tree.TreeLoader
18363  * @extends Roo.util.Observable
18364  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18365  * nodes from a specified URL. The response must be a javascript Array definition
18366  * who's elements are node definition objects. eg:
18367  * <pre><code>
18368    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18369     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18370 </code></pre>
18371  * <br><br>
18372  * A server request is sent, and child nodes are loaded only when a node is expanded.
18373  * The loading node's id is passed to the server under the parameter name "node" to
18374  * enable the server to produce the correct child nodes.
18375  * <br><br>
18376  * To pass extra parameters, an event handler may be attached to the "beforeload"
18377  * event, and the parameters specified in the TreeLoader's baseParams property:
18378  * <pre><code>
18379     myTreeLoader.on("beforeload", function(treeLoader, node) {
18380         this.baseParams.category = node.attributes.category;
18381     }, this);
18382 </code></pre><
18383  * This would pass an HTTP parameter called "category" to the server containing
18384  * the value of the Node's "category" attribute.
18385  * @constructor
18386  * Creates a new Treeloader.
18387  * @param {Object} config A config object containing config properties.
18388  */
18389 Roo.tree.TreeLoader = function(config){
18390     this.baseParams = {};
18391     this.requestMethod = "POST";
18392     Roo.apply(this, config);
18393
18394     this.addEvents({
18395     
18396         /**
18397          * @event beforeload
18398          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18399          * @param {Object} This TreeLoader object.
18400          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18401          * @param {Object} callback The callback function specified in the {@link #load} call.
18402          */
18403         beforeload : true,
18404         /**
18405          * @event load
18406          * Fires when the node has been successfuly loaded.
18407          * @param {Object} This TreeLoader object.
18408          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18409          * @param {Object} response The response object containing the data from the server.
18410          */
18411         load : true,
18412         /**
18413          * @event loadexception
18414          * Fires if the network request failed.
18415          * @param {Object} This TreeLoader object.
18416          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18417          * @param {Object} response The response object containing the data from the server.
18418          */
18419         loadexception : true,
18420         /**
18421          * @event create
18422          * Fires before a node is created, enabling you to return custom Node types 
18423          * @param {Object} This TreeLoader object.
18424          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18425          */
18426         create : true
18427     });
18428
18429     Roo.tree.TreeLoader.superclass.constructor.call(this);
18430 };
18431
18432 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18433     /**
18434     * @cfg {String} dataUrl The URL from which to request a Json string which
18435     * specifies an array of node definition object representing the child nodes
18436     * to be loaded.
18437     */
18438     /**
18439     * @cfg {Object} baseParams (optional) An object containing properties which
18440     * specify HTTP parameters to be passed to each request for child nodes.
18441     */
18442     /**
18443     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18444     * created by this loader. If the attributes sent by the server have an attribute in this object,
18445     * they take priority.
18446     */
18447     /**
18448     * @cfg {Object} uiProviders (optional) An object containing properties which
18449     * 
18450     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18451     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18452     * <i>uiProvider</i> attribute of a returned child node is a string rather
18453     * than a reference to a TreeNodeUI implementation, this that string value
18454     * is used as a property name in the uiProviders object. You can define the provider named
18455     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18456     */
18457     uiProviders : {},
18458
18459     /**
18460     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18461     * child nodes before loading.
18462     */
18463     clearOnLoad : true,
18464
18465     /**
18466     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18467     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18468     * Grid query { data : [ .....] }
18469     */
18470     
18471     root : false,
18472      /**
18473     * @cfg {String} queryParam (optional) 
18474     * Name of the query as it will be passed on the querystring (defaults to 'node')
18475     * eg. the request will be ?node=[id]
18476     */
18477     
18478     
18479     queryParam: false,
18480     
18481     /**
18482      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18483      * This is called automatically when a node is expanded, but may be used to reload
18484      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18485      * @param {Roo.tree.TreeNode} node
18486      * @param {Function} callback
18487      */
18488     load : function(node, callback){
18489         if(this.clearOnLoad){
18490             while(node.firstChild){
18491                 node.removeChild(node.firstChild);
18492             }
18493         }
18494         if(node.attributes.children){ // preloaded json children
18495             var cs = node.attributes.children;
18496             for(var i = 0, len = cs.length; i < len; i++){
18497                 node.appendChild(this.createNode(cs[i]));
18498             }
18499             if(typeof callback == "function"){
18500                 callback();
18501             }
18502         }else if(this.dataUrl){
18503             this.requestData(node, callback);
18504         }
18505     },
18506
18507     getParams: function(node){
18508         var buf = [], bp = this.baseParams;
18509         for(var key in bp){
18510             if(typeof bp[key] != "function"){
18511                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18512             }
18513         }
18514         var n = this.queryParam === false ? 'node' : this.queryParam;
18515         buf.push(n + "=", encodeURIComponent(node.id));
18516         return buf.join("");
18517     },
18518
18519     requestData : function(node, callback){
18520         if(this.fireEvent("beforeload", this, node, callback) !== false){
18521             this.transId = Roo.Ajax.request({
18522                 method:this.requestMethod,
18523                 url: this.dataUrl||this.url,
18524                 success: this.handleResponse,
18525                 failure: this.handleFailure,
18526                 scope: this,
18527                 argument: {callback: callback, node: node},
18528                 params: this.getParams(node)
18529             });
18530         }else{
18531             // if the load is cancelled, make sure we notify
18532             // the node that we are done
18533             if(typeof callback == "function"){
18534                 callback();
18535             }
18536         }
18537     },
18538
18539     isLoading : function(){
18540         return this.transId ? true : false;
18541     },
18542
18543     abort : function(){
18544         if(this.isLoading()){
18545             Roo.Ajax.abort(this.transId);
18546         }
18547     },
18548
18549     // private
18550     createNode : function(attr)
18551     {
18552         // apply baseAttrs, nice idea Corey!
18553         if(this.baseAttrs){
18554             Roo.applyIf(attr, this.baseAttrs);
18555         }
18556         if(this.applyLoader !== false){
18557             attr.loader = this;
18558         }
18559         // uiProvider = depreciated..
18560         
18561         if(typeof(attr.uiProvider) == 'string'){
18562            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18563                 /**  eval:var:attr */ eval(attr.uiProvider);
18564         }
18565         if(typeof(this.uiProviders['default']) != 'undefined') {
18566             attr.uiProvider = this.uiProviders['default'];
18567         }
18568         
18569         this.fireEvent('create', this, attr);
18570         
18571         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18572         return(attr.leaf ?
18573                         new Roo.tree.TreeNode(attr) :
18574                         new Roo.tree.AsyncTreeNode(attr));
18575     },
18576
18577     processResponse : function(response, node, callback)
18578     {
18579         var json = response.responseText;
18580         try {
18581             
18582             var o = Roo.decode(json);
18583             
18584             if (!o.success) {
18585                 // it's a failure condition.
18586                 var a = response.argument;
18587                 this.fireEvent("loadexception", this, a.node, response);
18588                 Roo.log("Load failed - should have a handler really");
18589                 return;
18590             }
18591             
18592             if (this.root !== false) {
18593                 o = o[this.root];
18594             }
18595             
18596             for(var i = 0, len = o.length; i < len; i++){
18597                 var n = this.createNode(o[i]);
18598                 if(n){
18599                     node.appendChild(n);
18600                 }
18601             }
18602             if(typeof callback == "function"){
18603                 callback(this, node);
18604             }
18605         }catch(e){
18606             this.handleFailure(response);
18607         }
18608     },
18609
18610     handleResponse : function(response){
18611         this.transId = false;
18612         var a = response.argument;
18613         this.processResponse(response, a.node, a.callback);
18614         this.fireEvent("load", this, a.node, response);
18615     },
18616
18617     handleFailure : function(response)
18618     {
18619         // should handle failure better..
18620         this.transId = false;
18621         var a = response.argument;
18622         this.fireEvent("loadexception", this, a.node, response);
18623         if(typeof a.callback == "function"){
18624             a.callback(this, a.node);
18625         }
18626     }
18627 });/*
18628  * Based on:
18629  * Ext JS Library 1.1.1
18630  * Copyright(c) 2006-2007, Ext JS, LLC.
18631  *
18632  * Originally Released Under LGPL - original licence link has changed is not relivant.
18633  *
18634  * Fork - LGPL
18635  * <script type="text/javascript">
18636  */
18637
18638 /**
18639 * @class Roo.tree.TreeFilter
18640 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18641 * @param {TreePanel} tree
18642 * @param {Object} config (optional)
18643  */
18644 Roo.tree.TreeFilter = function(tree, config){
18645     this.tree = tree;
18646     this.filtered = {};
18647     Roo.apply(this, config);
18648 };
18649
18650 Roo.tree.TreeFilter.prototype = {
18651     clearBlank:false,
18652     reverse:false,
18653     autoClear:false,
18654     remove:false,
18655
18656      /**
18657      * Filter the data by a specific attribute.
18658      * @param {String/RegExp} value Either string that the attribute value
18659      * should start with or a RegExp to test against the attribute
18660      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18661      * @param {TreeNode} startNode (optional) The node to start the filter at.
18662      */
18663     filter : function(value, attr, startNode){
18664         attr = attr || "text";
18665         var f;
18666         if(typeof value == "string"){
18667             var vlen = value.length;
18668             // auto clear empty filter
18669             if(vlen == 0 && this.clearBlank){
18670                 this.clear();
18671                 return;
18672             }
18673             value = value.toLowerCase();
18674             f = function(n){
18675                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18676             };
18677         }else if(value.exec){ // regex?
18678             f = function(n){
18679                 return value.test(n.attributes[attr]);
18680             };
18681         }else{
18682             throw 'Illegal filter type, must be string or regex';
18683         }
18684         this.filterBy(f, null, startNode);
18685         },
18686
18687     /**
18688      * Filter by a function. The passed function will be called with each
18689      * node in the tree (or from the startNode). If the function returns true, the node is kept
18690      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18691      * @param {Function} fn The filter function
18692      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18693      */
18694     filterBy : function(fn, scope, startNode){
18695         startNode = startNode || this.tree.root;
18696         if(this.autoClear){
18697             this.clear();
18698         }
18699         var af = this.filtered, rv = this.reverse;
18700         var f = function(n){
18701             if(n == startNode){
18702                 return true;
18703             }
18704             if(af[n.id]){
18705                 return false;
18706             }
18707             var m = fn.call(scope || n, n);
18708             if(!m || rv){
18709                 af[n.id] = n;
18710                 n.ui.hide();
18711                 return false;
18712             }
18713             return true;
18714         };
18715         startNode.cascade(f);
18716         if(this.remove){
18717            for(var id in af){
18718                if(typeof id != "function"){
18719                    var n = af[id];
18720                    if(n && n.parentNode){
18721                        n.parentNode.removeChild(n);
18722                    }
18723                }
18724            }
18725         }
18726     },
18727
18728     /**
18729      * Clears the current filter. Note: with the "remove" option
18730      * set a filter cannot be cleared.
18731      */
18732     clear : function(){
18733         var t = this.tree;
18734         var af = this.filtered;
18735         for(var id in af){
18736             if(typeof id != "function"){
18737                 var n = af[id];
18738                 if(n){
18739                     n.ui.show();
18740                 }
18741             }
18742         }
18743         this.filtered = {};
18744     }
18745 };
18746 /*
18747  * Based on:
18748  * Ext JS Library 1.1.1
18749  * Copyright(c) 2006-2007, Ext JS, LLC.
18750  *
18751  * Originally Released Under LGPL - original licence link has changed is not relivant.
18752  *
18753  * Fork - LGPL
18754  * <script type="text/javascript">
18755  */
18756  
18757
18758 /**
18759  * @class Roo.tree.TreeSorter
18760  * Provides sorting of nodes in a TreePanel
18761  * 
18762  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18763  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18764  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18765  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18766  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18767  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18768  * @constructor
18769  * @param {TreePanel} tree
18770  * @param {Object} config
18771  */
18772 Roo.tree.TreeSorter = function(tree, config){
18773     Roo.apply(this, config);
18774     tree.on("beforechildrenrendered", this.doSort, this);
18775     tree.on("append", this.updateSort, this);
18776     tree.on("insert", this.updateSort, this);
18777     
18778     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18779     var p = this.property || "text";
18780     var sortType = this.sortType;
18781     var fs = this.folderSort;
18782     var cs = this.caseSensitive === true;
18783     var leafAttr = this.leafAttr || 'leaf';
18784
18785     this.sortFn = function(n1, n2){
18786         if(fs){
18787             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18788                 return 1;
18789             }
18790             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18791                 return -1;
18792             }
18793         }
18794         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18795         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18796         if(v1 < v2){
18797                         return dsc ? +1 : -1;
18798                 }else if(v1 > v2){
18799                         return dsc ? -1 : +1;
18800         }else{
18801                 return 0;
18802         }
18803     };
18804 };
18805
18806 Roo.tree.TreeSorter.prototype = {
18807     doSort : function(node){
18808         node.sort(this.sortFn);
18809     },
18810     
18811     compareNodes : function(n1, n2){
18812         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18813     },
18814     
18815     updateSort : function(tree, node){
18816         if(node.childrenRendered){
18817             this.doSort.defer(1, this, [node]);
18818         }
18819     }
18820 };/*
18821  * Based on:
18822  * Ext JS Library 1.1.1
18823  * Copyright(c) 2006-2007, Ext JS, LLC.
18824  *
18825  * Originally Released Under LGPL - original licence link has changed is not relivant.
18826  *
18827  * Fork - LGPL
18828  * <script type="text/javascript">
18829  */
18830
18831 if(Roo.dd.DropZone){
18832     
18833 Roo.tree.TreeDropZone = function(tree, config){
18834     this.allowParentInsert = false;
18835     this.allowContainerDrop = false;
18836     this.appendOnly = false;
18837     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18838     this.tree = tree;
18839     this.lastInsertClass = "x-tree-no-status";
18840     this.dragOverData = {};
18841 };
18842
18843 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18844     ddGroup : "TreeDD",
18845     
18846     expandDelay : 1000,
18847     
18848     expandNode : function(node){
18849         if(node.hasChildNodes() && !node.isExpanded()){
18850             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18851         }
18852     },
18853     
18854     queueExpand : function(node){
18855         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18856     },
18857     
18858     cancelExpand : function(){
18859         if(this.expandProcId){
18860             clearTimeout(this.expandProcId);
18861             this.expandProcId = false;
18862         }
18863     },
18864     
18865     isValidDropPoint : function(n, pt, dd, e, data){
18866         if(!n || !data){ return false; }
18867         var targetNode = n.node;
18868         var dropNode = data.node;
18869         // default drop rules
18870         if(!(targetNode && targetNode.isTarget && pt)){
18871             return false;
18872         }
18873         if(pt == "append" && targetNode.allowChildren === false){
18874             return false;
18875         }
18876         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18877             return false;
18878         }
18879         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18880             return false;
18881         }
18882         // reuse the object
18883         var overEvent = this.dragOverData;
18884         overEvent.tree = this.tree;
18885         overEvent.target = targetNode;
18886         overEvent.data = data;
18887         overEvent.point = pt;
18888         overEvent.source = dd;
18889         overEvent.rawEvent = e;
18890         overEvent.dropNode = dropNode;
18891         overEvent.cancel = false;  
18892         var result = this.tree.fireEvent("nodedragover", overEvent);
18893         return overEvent.cancel === false && result !== false;
18894     },
18895     
18896     getDropPoint : function(e, n, dd){
18897         var tn = n.node;
18898         if(tn.isRoot){
18899             return tn.allowChildren !== false ? "append" : false; // always append for root
18900         }
18901         var dragEl = n.ddel;
18902         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18903         var y = Roo.lib.Event.getPageY(e);
18904         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18905         
18906         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18907         var noAppend = tn.allowChildren === false;
18908         if(this.appendOnly || tn.parentNode.allowChildren === false){
18909             return noAppend ? false : "append";
18910         }
18911         var noBelow = false;
18912         if(!this.allowParentInsert){
18913             noBelow = tn.hasChildNodes() && tn.isExpanded();
18914         }
18915         var q = (b - t) / (noAppend ? 2 : 3);
18916         if(y >= t && y < (t + q)){
18917             return "above";
18918         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18919             return "below";
18920         }else{
18921             return "append";
18922         }
18923     },
18924     
18925     onNodeEnter : function(n, dd, e, data){
18926         this.cancelExpand();
18927     },
18928     
18929     onNodeOver : function(n, dd, e, data){
18930         var pt = this.getDropPoint(e, n, dd);
18931         var node = n.node;
18932         
18933         // auto node expand check
18934         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18935             this.queueExpand(node);
18936         }else if(pt != "append"){
18937             this.cancelExpand();
18938         }
18939         
18940         // set the insert point style on the target node
18941         var returnCls = this.dropNotAllowed;
18942         if(this.isValidDropPoint(n, pt, dd, e, data)){
18943            if(pt){
18944                var el = n.ddel;
18945                var cls;
18946                if(pt == "above"){
18947                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18948                    cls = "x-tree-drag-insert-above";
18949                }else if(pt == "below"){
18950                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18951                    cls = "x-tree-drag-insert-below";
18952                }else{
18953                    returnCls = "x-tree-drop-ok-append";
18954                    cls = "x-tree-drag-append";
18955                }
18956                if(this.lastInsertClass != cls){
18957                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18958                    this.lastInsertClass = cls;
18959                }
18960            }
18961        }
18962        return returnCls;
18963     },
18964     
18965     onNodeOut : function(n, dd, e, data){
18966         this.cancelExpand();
18967         this.removeDropIndicators(n);
18968     },
18969     
18970     onNodeDrop : function(n, dd, e, data){
18971         var point = this.getDropPoint(e, n, dd);
18972         var targetNode = n.node;
18973         targetNode.ui.startDrop();
18974         if(!this.isValidDropPoint(n, point, dd, e, data)){
18975             targetNode.ui.endDrop();
18976             return false;
18977         }
18978         // first try to find the drop node
18979         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18980         var dropEvent = {
18981             tree : this.tree,
18982             target: targetNode,
18983             data: data,
18984             point: point,
18985             source: dd,
18986             rawEvent: e,
18987             dropNode: dropNode,
18988             cancel: !dropNode   
18989         };
18990         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18991         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18992             targetNode.ui.endDrop();
18993             return false;
18994         }
18995         // allow target changing
18996         targetNode = dropEvent.target;
18997         if(point == "append" && !targetNode.isExpanded()){
18998             targetNode.expand(false, null, function(){
18999                 this.completeDrop(dropEvent);
19000             }.createDelegate(this));
19001         }else{
19002             this.completeDrop(dropEvent);
19003         }
19004         return true;
19005     },
19006     
19007     completeDrop : function(de){
19008         var ns = de.dropNode, p = de.point, t = de.target;
19009         if(!(ns instanceof Array)){
19010             ns = [ns];
19011         }
19012         var n;
19013         for(var i = 0, len = ns.length; i < len; i++){
19014             n = ns[i];
19015             if(p == "above"){
19016                 t.parentNode.insertBefore(n, t);
19017             }else if(p == "below"){
19018                 t.parentNode.insertBefore(n, t.nextSibling);
19019             }else{
19020                 t.appendChild(n);
19021             }
19022         }
19023         n.ui.focus();
19024         if(this.tree.hlDrop){
19025             n.ui.highlight();
19026         }
19027         t.ui.endDrop();
19028         this.tree.fireEvent("nodedrop", de);
19029     },
19030     
19031     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19032         if(this.tree.hlDrop){
19033             dropNode.ui.focus();
19034             dropNode.ui.highlight();
19035         }
19036         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19037     },
19038     
19039     getTree : function(){
19040         return this.tree;
19041     },
19042     
19043     removeDropIndicators : function(n){
19044         if(n && n.ddel){
19045             var el = n.ddel;
19046             Roo.fly(el).removeClass([
19047                     "x-tree-drag-insert-above",
19048                     "x-tree-drag-insert-below",
19049                     "x-tree-drag-append"]);
19050             this.lastInsertClass = "_noclass";
19051         }
19052     },
19053     
19054     beforeDragDrop : function(target, e, id){
19055         this.cancelExpand();
19056         return true;
19057     },
19058     
19059     afterRepair : function(data){
19060         if(data && Roo.enableFx){
19061             data.node.ui.highlight();
19062         }
19063         this.hideProxy();
19064     }    
19065 });
19066
19067 }
19068 /*
19069  * Based on:
19070  * Ext JS Library 1.1.1
19071  * Copyright(c) 2006-2007, Ext JS, LLC.
19072  *
19073  * Originally Released Under LGPL - original licence link has changed is not relivant.
19074  *
19075  * Fork - LGPL
19076  * <script type="text/javascript">
19077  */
19078  
19079
19080 if(Roo.dd.DragZone){
19081 Roo.tree.TreeDragZone = function(tree, config){
19082     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19083     this.tree = tree;
19084 };
19085
19086 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19087     ddGroup : "TreeDD",
19088     
19089     onBeforeDrag : function(data, e){
19090         var n = data.node;
19091         return n && n.draggable && !n.disabled;
19092     },
19093     
19094     onInitDrag : function(e){
19095         var data = this.dragData;
19096         this.tree.getSelectionModel().select(data.node);
19097         this.proxy.update("");
19098         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19099         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19100     },
19101     
19102     getRepairXY : function(e, data){
19103         return data.node.ui.getDDRepairXY();
19104     },
19105     
19106     onEndDrag : function(data, e){
19107         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19108     },
19109     
19110     onValidDrop : function(dd, e, id){
19111         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19112         this.hideProxy();
19113     },
19114     
19115     beforeInvalidDrop : function(e, id){
19116         // this scrolls the original position back into view
19117         var sm = this.tree.getSelectionModel();
19118         sm.clearSelections();
19119         sm.select(this.dragData.node);
19120     }
19121 });
19122 }/*
19123  * Based on:
19124  * Ext JS Library 1.1.1
19125  * Copyright(c) 2006-2007, Ext JS, LLC.
19126  *
19127  * Originally Released Under LGPL - original licence link has changed is not relivant.
19128  *
19129  * Fork - LGPL
19130  * <script type="text/javascript">
19131  */
19132 /**
19133  * @class Roo.tree.TreeEditor
19134  * @extends Roo.Editor
19135  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19136  * as the editor field.
19137  * @constructor
19138  * @param {Object} config (used to be the tree panel.)
19139  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19140  * 
19141  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19142  * @cfg {Roo.form.TextField|Object} field The field configuration
19143  *
19144  * 
19145  */
19146 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19147     var tree = config;
19148     var field;
19149     if (oldconfig) { // old style..
19150         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19151     } else {
19152         // new style..
19153         tree = config.tree;
19154         config.field = config.field  || {};
19155         config.field.xtype = 'TextField';
19156         field = Roo.factory(config.field, Roo.form);
19157     }
19158     config = config || {};
19159     
19160     
19161     this.addEvents({
19162         /**
19163          * @event beforenodeedit
19164          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19165          * false from the handler of this event.
19166          * @param {Editor} this
19167          * @param {Roo.tree.Node} node 
19168          */
19169         "beforenodeedit" : true
19170     });
19171     
19172     //Roo.log(config);
19173     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19174
19175     this.tree = tree;
19176
19177     tree.on('beforeclick', this.beforeNodeClick, this);
19178     tree.getTreeEl().on('mousedown', this.hide, this);
19179     this.on('complete', this.updateNode, this);
19180     this.on('beforestartedit', this.fitToTree, this);
19181     this.on('startedit', this.bindScroll, this, {delay:10});
19182     this.on('specialkey', this.onSpecialKey, this);
19183 };
19184
19185 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19186     /**
19187      * @cfg {String} alignment
19188      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19189      */
19190     alignment: "l-l",
19191     // inherit
19192     autoSize: false,
19193     /**
19194      * @cfg {Boolean} hideEl
19195      * True to hide the bound element while the editor is displayed (defaults to false)
19196      */
19197     hideEl : false,
19198     /**
19199      * @cfg {String} cls
19200      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19201      */
19202     cls: "x-small-editor x-tree-editor",
19203     /**
19204      * @cfg {Boolean} shim
19205      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19206      */
19207     shim:false,
19208     // inherit
19209     shadow:"frame",
19210     /**
19211      * @cfg {Number} maxWidth
19212      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19213      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19214      * scroll and client offsets into account prior to each edit.
19215      */
19216     maxWidth: 250,
19217
19218     editDelay : 350,
19219
19220     // private
19221     fitToTree : function(ed, el){
19222         var td = this.tree.getTreeEl().dom, nd = el.dom;
19223         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19224             td.scrollLeft = nd.offsetLeft;
19225         }
19226         var w = Math.min(
19227                 this.maxWidth,
19228                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19229         this.setSize(w, '');
19230         
19231         return this.fireEvent('beforenodeedit', this, this.editNode);
19232         
19233     },
19234
19235     // private
19236     triggerEdit : function(node){
19237         this.completeEdit();
19238         this.editNode = node;
19239         this.startEdit(node.ui.textNode, node.text);
19240     },
19241
19242     // private
19243     bindScroll : function(){
19244         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19245     },
19246
19247     // private
19248     beforeNodeClick : function(node, e){
19249         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19250         this.lastClick = new Date();
19251         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19252             e.stopEvent();
19253             this.triggerEdit(node);
19254             return false;
19255         }
19256         return true;
19257     },
19258
19259     // private
19260     updateNode : function(ed, value){
19261         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19262         this.editNode.setText(value);
19263     },
19264
19265     // private
19266     onHide : function(){
19267         Roo.tree.TreeEditor.superclass.onHide.call(this);
19268         if(this.editNode){
19269             this.editNode.ui.focus();
19270         }
19271     },
19272
19273     // private
19274     onSpecialKey : function(field, e){
19275         var k = e.getKey();
19276         if(k == e.ESC){
19277             e.stopEvent();
19278             this.cancelEdit();
19279         }else if(k == e.ENTER && !e.hasModifier()){
19280             e.stopEvent();
19281             this.completeEdit();
19282         }
19283     }
19284 });//<Script type="text/javascript">
19285 /*
19286  * Based on:
19287  * Ext JS Library 1.1.1
19288  * Copyright(c) 2006-2007, Ext JS, LLC.
19289  *
19290  * Originally Released Under LGPL - original licence link has changed is not relivant.
19291  *
19292  * Fork - LGPL
19293  * <script type="text/javascript">
19294  */
19295  
19296 /**
19297  * Not documented??? - probably should be...
19298  */
19299
19300 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19301     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19302     
19303     renderElements : function(n, a, targetNode, bulkRender){
19304         //consel.log("renderElements?");
19305         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19306
19307         var t = n.getOwnerTree();
19308         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19309         
19310         var cols = t.columns;
19311         var bw = t.borderWidth;
19312         var c = cols[0];
19313         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19314          var cb = typeof a.checked == "boolean";
19315         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19316         var colcls = 'x-t-' + tid + '-c0';
19317         var buf = [
19318             '<li class="x-tree-node">',
19319             
19320                 
19321                 '<div class="x-tree-node-el ', a.cls,'">',
19322                     // extran...
19323                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19324                 
19325                 
19326                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19327                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19328                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19329                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19330                            (a.iconCls ? ' '+a.iconCls : ''),
19331                            '" unselectable="on" />',
19332                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19333                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19334                              
19335                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19336                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19337                             '<span unselectable="on" qtip="' + tx + '">',
19338                              tx,
19339                              '</span></a>' ,
19340                     '</div>',
19341                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19342                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19343                  ];
19344         for(var i = 1, len = cols.length; i < len; i++){
19345             c = cols[i];
19346             colcls = 'x-t-' + tid + '-c' +i;
19347             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19348             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19349                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19350                       "</div>");
19351          }
19352          
19353          buf.push(
19354             '</a>',
19355             '<div class="x-clear"></div></div>',
19356             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19357             "</li>");
19358         
19359         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19360             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19361                                 n.nextSibling.ui.getEl(), buf.join(""));
19362         }else{
19363             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19364         }
19365         var el = this.wrap.firstChild;
19366         this.elRow = el;
19367         this.elNode = el.firstChild;
19368         this.ranchor = el.childNodes[1];
19369         this.ctNode = this.wrap.childNodes[1];
19370         var cs = el.firstChild.childNodes;
19371         this.indentNode = cs[0];
19372         this.ecNode = cs[1];
19373         this.iconNode = cs[2];
19374         var index = 3;
19375         if(cb){
19376             this.checkbox = cs[3];
19377             index++;
19378         }
19379         this.anchor = cs[index];
19380         
19381         this.textNode = cs[index].firstChild;
19382         
19383         //el.on("click", this.onClick, this);
19384         //el.on("dblclick", this.onDblClick, this);
19385         
19386         
19387        // console.log(this);
19388     },
19389     initEvents : function(){
19390         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19391         
19392             
19393         var a = this.ranchor;
19394
19395         var el = Roo.get(a);
19396
19397         if(Roo.isOpera){ // opera render bug ignores the CSS
19398             el.setStyle("text-decoration", "none");
19399         }
19400
19401         el.on("click", this.onClick, this);
19402         el.on("dblclick", this.onDblClick, this);
19403         el.on("contextmenu", this.onContextMenu, this);
19404         
19405     },
19406     
19407     /*onSelectedChange : function(state){
19408         if(state){
19409             this.focus();
19410             this.addClass("x-tree-selected");
19411         }else{
19412             //this.blur();
19413             this.removeClass("x-tree-selected");
19414         }
19415     },*/
19416     addClass : function(cls){
19417         if(this.elRow){
19418             Roo.fly(this.elRow).addClass(cls);
19419         }
19420         
19421     },
19422     
19423     
19424     removeClass : function(cls){
19425         if(this.elRow){
19426             Roo.fly(this.elRow).removeClass(cls);
19427         }
19428     }
19429
19430     
19431     
19432 });//<Script type="text/javascript">
19433
19434 /*
19435  * Based on:
19436  * Ext JS Library 1.1.1
19437  * Copyright(c) 2006-2007, Ext JS, LLC.
19438  *
19439  * Originally Released Under LGPL - original licence link has changed is not relivant.
19440  *
19441  * Fork - LGPL
19442  * <script type="text/javascript">
19443  */
19444  
19445
19446 /**
19447  * @class Roo.tree.ColumnTree
19448  * @extends Roo.data.TreePanel
19449  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19450  * @cfg {int} borderWidth  compined right/left border allowance
19451  * @constructor
19452  * @param {String/HTMLElement/Element} el The container element
19453  * @param {Object} config
19454  */
19455 Roo.tree.ColumnTree =  function(el, config)
19456 {
19457    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19458    this.addEvents({
19459         /**
19460         * @event resize
19461         * Fire this event on a container when it resizes
19462         * @param {int} w Width
19463         * @param {int} h Height
19464         */
19465        "resize" : true
19466     });
19467     this.on('resize', this.onResize, this);
19468 };
19469
19470 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19471     //lines:false,
19472     
19473     
19474     borderWidth: Roo.isBorderBox ? 0 : 2, 
19475     headEls : false,
19476     
19477     render : function(){
19478         // add the header.....
19479        
19480         Roo.tree.ColumnTree.superclass.render.apply(this);
19481         
19482         this.el.addClass('x-column-tree');
19483         
19484         this.headers = this.el.createChild(
19485             {cls:'x-tree-headers'},this.innerCt.dom);
19486    
19487         var cols = this.columns, c;
19488         var totalWidth = 0;
19489         this.headEls = [];
19490         var  len = cols.length;
19491         for(var i = 0; i < len; i++){
19492              c = cols[i];
19493              totalWidth += c.width;
19494             this.headEls.push(this.headers.createChild({
19495                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19496                  cn: {
19497                      cls:'x-tree-hd-text',
19498                      html: c.header
19499                  },
19500                  style:'width:'+(c.width-this.borderWidth)+'px;'
19501              }));
19502         }
19503         this.headers.createChild({cls:'x-clear'});
19504         // prevent floats from wrapping when clipped
19505         this.headers.setWidth(totalWidth);
19506         //this.innerCt.setWidth(totalWidth);
19507         this.innerCt.setStyle({ overflow: 'auto' });
19508         this.onResize(this.width, this.height);
19509              
19510         
19511     },
19512     onResize : function(w,h)
19513     {
19514         this.height = h;
19515         this.width = w;
19516         // resize cols..
19517         this.innerCt.setWidth(this.width);
19518         this.innerCt.setHeight(this.height-20);
19519         
19520         // headers...
19521         var cols = this.columns, c;
19522         var totalWidth = 0;
19523         var expEl = false;
19524         var len = cols.length;
19525         for(var i = 0; i < len; i++){
19526             c = cols[i];
19527             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19528                 // it's the expander..
19529                 expEl  = this.headEls[i];
19530                 continue;
19531             }
19532             totalWidth += c.width;
19533             
19534         }
19535         if (expEl) {
19536             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19537         }
19538         this.headers.setWidth(w-20);
19539
19540         
19541         
19542         
19543     }
19544 });
19545 /*
19546  * Based on:
19547  * Ext JS Library 1.1.1
19548  * Copyright(c) 2006-2007, Ext JS, LLC.
19549  *
19550  * Originally Released Under LGPL - original licence link has changed is not relivant.
19551  *
19552  * Fork - LGPL
19553  * <script type="text/javascript">
19554  */
19555  
19556 /**
19557  * @class Roo.menu.Menu
19558  * @extends Roo.util.Observable
19559  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19560  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19561  * @constructor
19562  * Creates a new Menu
19563  * @param {Object} config Configuration options
19564  */
19565 Roo.menu.Menu = function(config){
19566     Roo.apply(this, config);
19567     this.id = this.id || Roo.id();
19568     this.addEvents({
19569         /**
19570          * @event beforeshow
19571          * Fires before this menu is displayed
19572          * @param {Roo.menu.Menu} this
19573          */
19574         beforeshow : true,
19575         /**
19576          * @event beforehide
19577          * Fires before this menu is hidden
19578          * @param {Roo.menu.Menu} this
19579          */
19580         beforehide : true,
19581         /**
19582          * @event show
19583          * Fires after this menu is displayed
19584          * @param {Roo.menu.Menu} this
19585          */
19586         show : true,
19587         /**
19588          * @event hide
19589          * Fires after this menu is hidden
19590          * @param {Roo.menu.Menu} this
19591          */
19592         hide : true,
19593         /**
19594          * @event click
19595          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19596          * @param {Roo.menu.Menu} this
19597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19598          * @param {Roo.EventObject} e
19599          */
19600         click : true,
19601         /**
19602          * @event mouseover
19603          * Fires when the mouse is hovering over this menu
19604          * @param {Roo.menu.Menu} this
19605          * @param {Roo.EventObject} e
19606          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19607          */
19608         mouseover : true,
19609         /**
19610          * @event mouseout
19611          * Fires when the mouse exits this menu
19612          * @param {Roo.menu.Menu} this
19613          * @param {Roo.EventObject} e
19614          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19615          */
19616         mouseout : true,
19617         /**
19618          * @event itemclick
19619          * Fires when a menu item contained in this menu is clicked
19620          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19621          * @param {Roo.EventObject} e
19622          */
19623         itemclick: true
19624     });
19625     if (this.registerMenu) {
19626         Roo.menu.MenuMgr.register(this);
19627     }
19628     
19629     var mis = this.items;
19630     this.items = new Roo.util.MixedCollection();
19631     if(mis){
19632         this.add.apply(this, mis);
19633     }
19634 };
19635
19636 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19637     /**
19638      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19639      */
19640     minWidth : 120,
19641     /**
19642      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19643      * for bottom-right shadow (defaults to "sides")
19644      */
19645     shadow : "sides",
19646     /**
19647      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19648      * this menu (defaults to "tl-tr?")
19649      */
19650     subMenuAlign : "tl-tr?",
19651     /**
19652      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19653      * relative to its element of origin (defaults to "tl-bl?")
19654      */
19655     defaultAlign : "tl-bl?",
19656     /**
19657      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19658      */
19659     allowOtherMenus : false,
19660     /**
19661      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19662      */
19663     registerMenu : true,
19664
19665     hidden:true,
19666
19667     // private
19668     render : function(){
19669         if(this.el){
19670             return;
19671         }
19672         var el = this.el = new Roo.Layer({
19673             cls: "x-menu",
19674             shadow:this.shadow,
19675             constrain: false,
19676             parentEl: this.parentEl || document.body,
19677             zindex:15000
19678         });
19679
19680         this.keyNav = new Roo.menu.MenuNav(this);
19681
19682         if(this.plain){
19683             el.addClass("x-menu-plain");
19684         }
19685         if(this.cls){
19686             el.addClass(this.cls);
19687         }
19688         // generic focus element
19689         this.focusEl = el.createChild({
19690             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19691         });
19692         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19693         ul.on("click", this.onClick, this);
19694         ul.on("mouseover", this.onMouseOver, this);
19695         ul.on("mouseout", this.onMouseOut, this);
19696         this.items.each(function(item){
19697             var li = document.createElement("li");
19698             li.className = "x-menu-list-item";
19699             ul.dom.appendChild(li);
19700             item.render(li, this);
19701         }, this);
19702         this.ul = ul;
19703         this.autoWidth();
19704     },
19705
19706     // private
19707     autoWidth : function(){
19708         var el = this.el, ul = this.ul;
19709         if(!el){
19710             return;
19711         }
19712         var w = this.width;
19713         if(w){
19714             el.setWidth(w);
19715         }else if(Roo.isIE){
19716             el.setWidth(this.minWidth);
19717             var t = el.dom.offsetWidth; // force recalc
19718             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19719         }
19720     },
19721
19722     // private
19723     delayAutoWidth : function(){
19724         if(this.rendered){
19725             if(!this.awTask){
19726                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19727             }
19728             this.awTask.delay(20);
19729         }
19730     },
19731
19732     // private
19733     findTargetItem : function(e){
19734         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19735         if(t && t.menuItemId){
19736             return this.items.get(t.menuItemId);
19737         }
19738     },
19739
19740     // private
19741     onClick : function(e){
19742         var t;
19743         if(t = this.findTargetItem(e)){
19744             t.onClick(e);
19745             this.fireEvent("click", this, t, e);
19746         }
19747     },
19748
19749     // private
19750     setActiveItem : function(item, autoExpand){
19751         if(item != this.activeItem){
19752             if(this.activeItem){
19753                 this.activeItem.deactivate();
19754             }
19755             this.activeItem = item;
19756             item.activate(autoExpand);
19757         }else if(autoExpand){
19758             item.expandMenu();
19759         }
19760     },
19761
19762     // private
19763     tryActivate : function(start, step){
19764         var items = this.items;
19765         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19766             var item = items.get(i);
19767             if(!item.disabled && item.canActivate){
19768                 this.setActiveItem(item, false);
19769                 return item;
19770             }
19771         }
19772         return false;
19773     },
19774
19775     // private
19776     onMouseOver : function(e){
19777         var t;
19778         if(t = this.findTargetItem(e)){
19779             if(t.canActivate && !t.disabled){
19780                 this.setActiveItem(t, true);
19781             }
19782         }
19783         this.fireEvent("mouseover", this, e, t);
19784     },
19785
19786     // private
19787     onMouseOut : function(e){
19788         var t;
19789         if(t = this.findTargetItem(e)){
19790             if(t == this.activeItem && t.shouldDeactivate(e)){
19791                 this.activeItem.deactivate();
19792                 delete this.activeItem;
19793             }
19794         }
19795         this.fireEvent("mouseout", this, e, t);
19796     },
19797
19798     /**
19799      * Read-only.  Returns true if the menu is currently displayed, else false.
19800      * @type Boolean
19801      */
19802     isVisible : function(){
19803         return this.el && !this.hidden;
19804     },
19805
19806     /**
19807      * Displays this menu relative to another element
19808      * @param {String/HTMLElement/Roo.Element} element The element to align to
19809      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19810      * the element (defaults to this.defaultAlign)
19811      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19812      */
19813     show : function(el, pos, parentMenu){
19814         this.parentMenu = parentMenu;
19815         if(!this.el){
19816             this.render();
19817         }
19818         this.fireEvent("beforeshow", this);
19819         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19820     },
19821
19822     /**
19823      * Displays this menu at a specific xy position
19824      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19825      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19826      */
19827     showAt : function(xy, parentMenu, /* private: */_e){
19828         this.parentMenu = parentMenu;
19829         if(!this.el){
19830             this.render();
19831         }
19832         if(_e !== false){
19833             this.fireEvent("beforeshow", this);
19834             xy = this.el.adjustForConstraints(xy);
19835         }
19836         this.el.setXY(xy);
19837         this.el.show();
19838         this.hidden = false;
19839         this.focus();
19840         this.fireEvent("show", this);
19841     },
19842
19843     focus : function(){
19844         if(!this.hidden){
19845             this.doFocus.defer(50, this);
19846         }
19847     },
19848
19849     doFocus : function(){
19850         if(!this.hidden){
19851             this.focusEl.focus();
19852         }
19853     },
19854
19855     /**
19856      * Hides this menu and optionally all parent menus
19857      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19858      */
19859     hide : function(deep){
19860         if(this.el && this.isVisible()){
19861             this.fireEvent("beforehide", this);
19862             if(this.activeItem){
19863                 this.activeItem.deactivate();
19864                 this.activeItem = null;
19865             }
19866             this.el.hide();
19867             this.hidden = true;
19868             this.fireEvent("hide", this);
19869         }
19870         if(deep === true && this.parentMenu){
19871             this.parentMenu.hide(true);
19872         }
19873     },
19874
19875     /**
19876      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19877      * Any of the following are valid:
19878      * <ul>
19879      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19880      * <li>An HTMLElement object which will be converted to a menu item</li>
19881      * <li>A menu item config object that will be created as a new menu item</li>
19882      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19883      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19884      * </ul>
19885      * Usage:
19886      * <pre><code>
19887 // Create the menu
19888 var menu = new Roo.menu.Menu();
19889
19890 // Create a menu item to add by reference
19891 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19892
19893 // Add a bunch of items at once using different methods.
19894 // Only the last item added will be returned.
19895 var item = menu.add(
19896     menuItem,                // add existing item by ref
19897     'Dynamic Item',          // new TextItem
19898     '-',                     // new separator
19899     { text: 'Config Item' }  // new item by config
19900 );
19901 </code></pre>
19902      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19903      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19904      */
19905     add : function(){
19906         var a = arguments, l = a.length, item;
19907         for(var i = 0; i < l; i++){
19908             var el = a[i];
19909             if ((typeof(el) == "object") && el.xtype && el.xns) {
19910                 el = Roo.factory(el, Roo.menu);
19911             }
19912             
19913             if(el.render){ // some kind of Item
19914                 item = this.addItem(el);
19915             }else if(typeof el == "string"){ // string
19916                 if(el == "separator" || el == "-"){
19917                     item = this.addSeparator();
19918                 }else{
19919                     item = this.addText(el);
19920                 }
19921             }else if(el.tagName || el.el){ // element
19922                 item = this.addElement(el);
19923             }else if(typeof el == "object"){ // must be menu item config?
19924                 item = this.addMenuItem(el);
19925             }
19926         }
19927         return item;
19928     },
19929
19930     /**
19931      * Returns this menu's underlying {@link Roo.Element} object
19932      * @return {Roo.Element} The element
19933      */
19934     getEl : function(){
19935         if(!this.el){
19936             this.render();
19937         }
19938         return this.el;
19939     },
19940
19941     /**
19942      * Adds a separator bar to the menu
19943      * @return {Roo.menu.Item} The menu item that was added
19944      */
19945     addSeparator : function(){
19946         return this.addItem(new Roo.menu.Separator());
19947     },
19948
19949     /**
19950      * Adds an {@link Roo.Element} object to the menu
19951      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19952      * @return {Roo.menu.Item} The menu item that was added
19953      */
19954     addElement : function(el){
19955         return this.addItem(new Roo.menu.BaseItem(el));
19956     },
19957
19958     /**
19959      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19960      * @param {Roo.menu.Item} item The menu item to add
19961      * @return {Roo.menu.Item} The menu item that was added
19962      */
19963     addItem : function(item){
19964         this.items.add(item);
19965         if(this.ul){
19966             var li = document.createElement("li");
19967             li.className = "x-menu-list-item";
19968             this.ul.dom.appendChild(li);
19969             item.render(li, this);
19970             this.delayAutoWidth();
19971         }
19972         return item;
19973     },
19974
19975     /**
19976      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19977      * @param {Object} config A MenuItem config object
19978      * @return {Roo.menu.Item} The menu item that was added
19979      */
19980     addMenuItem : function(config){
19981         if(!(config instanceof Roo.menu.Item)){
19982             if(typeof config.checked == "boolean"){ // must be check menu item config?
19983                 config = new Roo.menu.CheckItem(config);
19984             }else{
19985                 config = new Roo.menu.Item(config);
19986             }
19987         }
19988         return this.addItem(config);
19989     },
19990
19991     /**
19992      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19993      * @param {String} text The text to display in the menu item
19994      * @return {Roo.menu.Item} The menu item that was added
19995      */
19996     addText : function(text){
19997         return this.addItem(new Roo.menu.TextItem({ text : text }));
19998     },
19999
20000     /**
20001      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20002      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20003      * @param {Roo.menu.Item} item The menu item to add
20004      * @return {Roo.menu.Item} The menu item that was added
20005      */
20006     insert : function(index, item){
20007         this.items.insert(index, item);
20008         if(this.ul){
20009             var li = document.createElement("li");
20010             li.className = "x-menu-list-item";
20011             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20012             item.render(li, this);
20013             this.delayAutoWidth();
20014         }
20015         return item;
20016     },
20017
20018     /**
20019      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20020      * @param {Roo.menu.Item} item The menu item to remove
20021      */
20022     remove : function(item){
20023         this.items.removeKey(item.id);
20024         item.destroy();
20025     },
20026
20027     /**
20028      * Removes and destroys all items in the menu
20029      */
20030     removeAll : function(){
20031         var f;
20032         while(f = this.items.first()){
20033             this.remove(f);
20034         }
20035     }
20036 });
20037
20038 // MenuNav is a private utility class used internally by the Menu
20039 Roo.menu.MenuNav = function(menu){
20040     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20041     this.scope = this.menu = menu;
20042 };
20043
20044 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20045     doRelay : function(e, h){
20046         var k = e.getKey();
20047         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20048             this.menu.tryActivate(0, 1);
20049             return false;
20050         }
20051         return h.call(this.scope || this, e, this.menu);
20052     },
20053
20054     up : function(e, m){
20055         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20056             m.tryActivate(m.items.length-1, -1);
20057         }
20058     },
20059
20060     down : function(e, m){
20061         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20062             m.tryActivate(0, 1);
20063         }
20064     },
20065
20066     right : function(e, m){
20067         if(m.activeItem){
20068             m.activeItem.expandMenu(true);
20069         }
20070     },
20071
20072     left : function(e, m){
20073         m.hide();
20074         if(m.parentMenu && m.parentMenu.activeItem){
20075             m.parentMenu.activeItem.activate();
20076         }
20077     },
20078
20079     enter : function(e, m){
20080         if(m.activeItem){
20081             e.stopPropagation();
20082             m.activeItem.onClick(e);
20083             m.fireEvent("click", this, m.activeItem);
20084             return true;
20085         }
20086     }
20087 });/*
20088  * Based on:
20089  * Ext JS Library 1.1.1
20090  * Copyright(c) 2006-2007, Ext JS, LLC.
20091  *
20092  * Originally Released Under LGPL - original licence link has changed is not relivant.
20093  *
20094  * Fork - LGPL
20095  * <script type="text/javascript">
20096  */
20097  
20098 /**
20099  * @class Roo.menu.MenuMgr
20100  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20101  * @singleton
20102  */
20103 Roo.menu.MenuMgr = function(){
20104    var menus, active, groups = {}, attached = false, lastShow = new Date();
20105
20106    // private - called when first menu is created
20107    function init(){
20108        menus = {};
20109        active = new Roo.util.MixedCollection();
20110        Roo.get(document).addKeyListener(27, function(){
20111            if(active.length > 0){
20112                hideAll();
20113            }
20114        });
20115    }
20116
20117    // private
20118    function hideAll(){
20119        if(active && active.length > 0){
20120            var c = active.clone();
20121            c.each(function(m){
20122                m.hide();
20123            });
20124        }
20125    }
20126
20127    // private
20128    function onHide(m){
20129        active.remove(m);
20130        if(active.length < 1){
20131            Roo.get(document).un("mousedown", onMouseDown);
20132            attached = false;
20133        }
20134    }
20135
20136    // private
20137    function onShow(m){
20138        var last = active.last();
20139        lastShow = new Date();
20140        active.add(m);
20141        if(!attached){
20142            Roo.get(document).on("mousedown", onMouseDown);
20143            attached = true;
20144        }
20145        if(m.parentMenu){
20146           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20147           m.parentMenu.activeChild = m;
20148        }else if(last && last.isVisible()){
20149           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20150        }
20151    }
20152
20153    // private
20154    function onBeforeHide(m){
20155        if(m.activeChild){
20156            m.activeChild.hide();
20157        }
20158        if(m.autoHideTimer){
20159            clearTimeout(m.autoHideTimer);
20160            delete m.autoHideTimer;
20161        }
20162    }
20163
20164    // private
20165    function onBeforeShow(m){
20166        var pm = m.parentMenu;
20167        if(!pm && !m.allowOtherMenus){
20168            hideAll();
20169        }else if(pm && pm.activeChild && active != m){
20170            pm.activeChild.hide();
20171        }
20172    }
20173
20174    // private
20175    function onMouseDown(e){
20176        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20177            hideAll();
20178        }
20179    }
20180
20181    // private
20182    function onBeforeCheck(mi, state){
20183        if(state){
20184            var g = groups[mi.group];
20185            for(var i = 0, l = g.length; i < l; i++){
20186                if(g[i] != mi){
20187                    g[i].setChecked(false);
20188                }
20189            }
20190        }
20191    }
20192
20193    return {
20194
20195        /**
20196         * Hides all menus that are currently visible
20197         */
20198        hideAll : function(){
20199             hideAll();  
20200        },
20201
20202        // private
20203        register : function(menu){
20204            if(!menus){
20205                init();
20206            }
20207            menus[menu.id] = menu;
20208            menu.on("beforehide", onBeforeHide);
20209            menu.on("hide", onHide);
20210            menu.on("beforeshow", onBeforeShow);
20211            menu.on("show", onShow);
20212            var g = menu.group;
20213            if(g && menu.events["checkchange"]){
20214                if(!groups[g]){
20215                    groups[g] = [];
20216                }
20217                groups[g].push(menu);
20218                menu.on("checkchange", onCheck);
20219            }
20220        },
20221
20222         /**
20223          * Returns a {@link Roo.menu.Menu} object
20224          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20225          * be used to generate and return a new Menu instance.
20226          */
20227        get : function(menu){
20228            if(typeof menu == "string"){ // menu id
20229                return menus[menu];
20230            }else if(menu.events){  // menu instance
20231                return menu;
20232            }else if(typeof menu.length == 'number'){ // array of menu items?
20233                return new Roo.menu.Menu({items:menu});
20234            }else{ // otherwise, must be a config
20235                return new Roo.menu.Menu(menu);
20236            }
20237        },
20238
20239        // private
20240        unregister : function(menu){
20241            delete menus[menu.id];
20242            menu.un("beforehide", onBeforeHide);
20243            menu.un("hide", onHide);
20244            menu.un("beforeshow", onBeforeShow);
20245            menu.un("show", onShow);
20246            var g = menu.group;
20247            if(g && menu.events["checkchange"]){
20248                groups[g].remove(menu);
20249                menu.un("checkchange", onCheck);
20250            }
20251        },
20252
20253        // private
20254        registerCheckable : function(menuItem){
20255            var g = menuItem.group;
20256            if(g){
20257                if(!groups[g]){
20258                    groups[g] = [];
20259                }
20260                groups[g].push(menuItem);
20261                menuItem.on("beforecheckchange", onBeforeCheck);
20262            }
20263        },
20264
20265        // private
20266        unregisterCheckable : function(menuItem){
20267            var g = menuItem.group;
20268            if(g){
20269                groups[g].remove(menuItem);
20270                menuItem.un("beforecheckchange", onBeforeCheck);
20271            }
20272        }
20273    };
20274 }();/*
20275  * Based on:
20276  * Ext JS Library 1.1.1
20277  * Copyright(c) 2006-2007, Ext JS, LLC.
20278  *
20279  * Originally Released Under LGPL - original licence link has changed is not relivant.
20280  *
20281  * Fork - LGPL
20282  * <script type="text/javascript">
20283  */
20284  
20285
20286 /**
20287  * @class Roo.menu.BaseItem
20288  * @extends Roo.Component
20289  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20290  * management and base configuration options shared by all menu components.
20291  * @constructor
20292  * Creates a new BaseItem
20293  * @param {Object} config Configuration options
20294  */
20295 Roo.menu.BaseItem = function(config){
20296     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20297
20298     this.addEvents({
20299         /**
20300          * @event click
20301          * Fires when this item is clicked
20302          * @param {Roo.menu.BaseItem} this
20303          * @param {Roo.EventObject} e
20304          */
20305         click: true,
20306         /**
20307          * @event activate
20308          * Fires when this item is activated
20309          * @param {Roo.menu.BaseItem} this
20310          */
20311         activate : true,
20312         /**
20313          * @event deactivate
20314          * Fires when this item is deactivated
20315          * @param {Roo.menu.BaseItem} this
20316          */
20317         deactivate : true
20318     });
20319
20320     if(this.handler){
20321         this.on("click", this.handler, this.scope, true);
20322     }
20323 };
20324
20325 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20326     /**
20327      * @cfg {Function} handler
20328      * A function that will handle the click event of this menu item (defaults to undefined)
20329      */
20330     /**
20331      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20332      */
20333     canActivate : false,
20334     /**
20335      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20336      */
20337     activeClass : "x-menu-item-active",
20338     /**
20339      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20340      */
20341     hideOnClick : true,
20342     /**
20343      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20344      */
20345     hideDelay : 100,
20346
20347     // private
20348     ctype: "Roo.menu.BaseItem",
20349
20350     // private
20351     actionMode : "container",
20352
20353     // private
20354     render : function(container, parentMenu){
20355         this.parentMenu = parentMenu;
20356         Roo.menu.BaseItem.superclass.render.call(this, container);
20357         this.container.menuItemId = this.id;
20358     },
20359
20360     // private
20361     onRender : function(container, position){
20362         this.el = Roo.get(this.el);
20363         container.dom.appendChild(this.el.dom);
20364     },
20365
20366     // private
20367     onClick : function(e){
20368         if(!this.disabled && this.fireEvent("click", this, e) !== false
20369                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20370             this.handleClick(e);
20371         }else{
20372             e.stopEvent();
20373         }
20374     },
20375
20376     // private
20377     activate : function(){
20378         if(this.disabled){
20379             return false;
20380         }
20381         var li = this.container;
20382         li.addClass(this.activeClass);
20383         this.region = li.getRegion().adjust(2, 2, -2, -2);
20384         this.fireEvent("activate", this);
20385         return true;
20386     },
20387
20388     // private
20389     deactivate : function(){
20390         this.container.removeClass(this.activeClass);
20391         this.fireEvent("deactivate", this);
20392     },
20393
20394     // private
20395     shouldDeactivate : function(e){
20396         return !this.region || !this.region.contains(e.getPoint());
20397     },
20398
20399     // private
20400     handleClick : function(e){
20401         if(this.hideOnClick){
20402             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20403         }
20404     },
20405
20406     // private
20407     expandMenu : function(autoActivate){
20408         // do nothing
20409     },
20410
20411     // private
20412     hideMenu : function(){
20413         // do nothing
20414     }
20415 });/*
20416  * Based on:
20417  * Ext JS Library 1.1.1
20418  * Copyright(c) 2006-2007, Ext JS, LLC.
20419  *
20420  * Originally Released Under LGPL - original licence link has changed is not relivant.
20421  *
20422  * Fork - LGPL
20423  * <script type="text/javascript">
20424  */
20425  
20426 /**
20427  * @class Roo.menu.Adapter
20428  * @extends Roo.menu.BaseItem
20429  * 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.
20430  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20431  * @constructor
20432  * Creates a new Adapter
20433  * @param {Object} config Configuration options
20434  */
20435 Roo.menu.Adapter = function(component, config){
20436     Roo.menu.Adapter.superclass.constructor.call(this, config);
20437     this.component = component;
20438 };
20439 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20440     // private
20441     canActivate : true,
20442
20443     // private
20444     onRender : function(container, position){
20445         this.component.render(container);
20446         this.el = this.component.getEl();
20447     },
20448
20449     // private
20450     activate : function(){
20451         if(this.disabled){
20452             return false;
20453         }
20454         this.component.focus();
20455         this.fireEvent("activate", this);
20456         return true;
20457     },
20458
20459     // private
20460     deactivate : function(){
20461         this.fireEvent("deactivate", this);
20462     },
20463
20464     // private
20465     disable : function(){
20466         this.component.disable();
20467         Roo.menu.Adapter.superclass.disable.call(this);
20468     },
20469
20470     // private
20471     enable : function(){
20472         this.component.enable();
20473         Roo.menu.Adapter.superclass.enable.call(this);
20474     }
20475 });/*
20476  * Based on:
20477  * Ext JS Library 1.1.1
20478  * Copyright(c) 2006-2007, Ext JS, LLC.
20479  *
20480  * Originally Released Under LGPL - original licence link has changed is not relivant.
20481  *
20482  * Fork - LGPL
20483  * <script type="text/javascript">
20484  */
20485
20486 /**
20487  * @class Roo.menu.TextItem
20488  * @extends Roo.menu.BaseItem
20489  * Adds a static text string to a menu, usually used as either a heading or group separator.
20490  * Note: old style constructor with text is still supported.
20491  * 
20492  * @constructor
20493  * Creates a new TextItem
20494  * @param {Object} cfg Configuration
20495  */
20496 Roo.menu.TextItem = function(cfg){
20497     if (typeof(cfg) == 'string') {
20498         this.text = cfg;
20499     } else {
20500         Roo.apply(this,cfg);
20501     }
20502     
20503     Roo.menu.TextItem.superclass.constructor.call(this);
20504 };
20505
20506 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20507     /**
20508      * @cfg {Boolean} text Text to show on item.
20509      */
20510     text : '',
20511     
20512     /**
20513      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20514      */
20515     hideOnClick : false,
20516     /**
20517      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20518      */
20519     itemCls : "x-menu-text",
20520
20521     // private
20522     onRender : function(){
20523         var s = document.createElement("span");
20524         s.className = this.itemCls;
20525         s.innerHTML = this.text;
20526         this.el = s;
20527         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20528     }
20529 });/*
20530  * Based on:
20531  * Ext JS Library 1.1.1
20532  * Copyright(c) 2006-2007, Ext JS, LLC.
20533  *
20534  * Originally Released Under LGPL - original licence link has changed is not relivant.
20535  *
20536  * Fork - LGPL
20537  * <script type="text/javascript">
20538  */
20539
20540 /**
20541  * @class Roo.menu.Separator
20542  * @extends Roo.menu.BaseItem
20543  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20544  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20545  * @constructor
20546  * @param {Object} config Configuration options
20547  */
20548 Roo.menu.Separator = function(config){
20549     Roo.menu.Separator.superclass.constructor.call(this, config);
20550 };
20551
20552 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20553     /**
20554      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20555      */
20556     itemCls : "x-menu-sep",
20557     /**
20558      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20559      */
20560     hideOnClick : false,
20561
20562     // private
20563     onRender : function(li){
20564         var s = document.createElement("span");
20565         s.className = this.itemCls;
20566         s.innerHTML = "&#160;";
20567         this.el = s;
20568         li.addClass("x-menu-sep-li");
20569         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20570     }
20571 });/*
20572  * Based on:
20573  * Ext JS Library 1.1.1
20574  * Copyright(c) 2006-2007, Ext JS, LLC.
20575  *
20576  * Originally Released Under LGPL - original licence link has changed is not relivant.
20577  *
20578  * Fork - LGPL
20579  * <script type="text/javascript">
20580  */
20581 /**
20582  * @class Roo.menu.Item
20583  * @extends Roo.menu.BaseItem
20584  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20585  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20586  * activation and click handling.
20587  * @constructor
20588  * Creates a new Item
20589  * @param {Object} config Configuration options
20590  */
20591 Roo.menu.Item = function(config){
20592     Roo.menu.Item.superclass.constructor.call(this, config);
20593     if(this.menu){
20594         this.menu = Roo.menu.MenuMgr.get(this.menu);
20595     }
20596 };
20597 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20598     
20599     /**
20600      * @cfg {String} text
20601      * The text to show on the menu item.
20602      */
20603     text: '',
20604      /**
20605      * @cfg {String} HTML to render in menu
20606      * The text to show on the menu item (HTML version).
20607      */
20608     html: '',
20609     /**
20610      * @cfg {String} icon
20611      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20612      */
20613     icon: undefined,
20614     /**
20615      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20616      */
20617     itemCls : "x-menu-item",
20618     /**
20619      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20620      */
20621     canActivate : true,
20622     /**
20623      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20624      */
20625     showDelay: 200,
20626     // doc'd in BaseItem
20627     hideDelay: 200,
20628
20629     // private
20630     ctype: "Roo.menu.Item",
20631     
20632     // private
20633     onRender : function(container, position){
20634         var el = document.createElement("a");
20635         el.hideFocus = true;
20636         el.unselectable = "on";
20637         el.href = this.href || "#";
20638         if(this.hrefTarget){
20639             el.target = this.hrefTarget;
20640         }
20641         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20642         
20643         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20644         
20645         el.innerHTML = String.format(
20646                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20647                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20648         this.el = el;
20649         Roo.menu.Item.superclass.onRender.call(this, container, position);
20650     },
20651
20652     /**
20653      * Sets the text to display in this menu item
20654      * @param {String} text The text to display
20655      * @param {Boolean} isHTML true to indicate text is pure html.
20656      */
20657     setText : function(text, isHTML){
20658         if (isHTML) {
20659             this.html = text;
20660         } else {
20661             this.text = text;
20662             this.html = '';
20663         }
20664         if(this.rendered){
20665             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20666      
20667             this.el.update(String.format(
20668                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20669                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20670             this.parentMenu.autoWidth();
20671         }
20672     },
20673
20674     // private
20675     handleClick : function(e){
20676         if(!this.href){ // if no link defined, stop the event automatically
20677             e.stopEvent();
20678         }
20679         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20680     },
20681
20682     // private
20683     activate : function(autoExpand){
20684         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20685             this.focus();
20686             if(autoExpand){
20687                 this.expandMenu();
20688             }
20689         }
20690         return true;
20691     },
20692
20693     // private
20694     shouldDeactivate : function(e){
20695         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20696             if(this.menu && this.menu.isVisible()){
20697                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20698             }
20699             return true;
20700         }
20701         return false;
20702     },
20703
20704     // private
20705     deactivate : function(){
20706         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20707         this.hideMenu();
20708     },
20709
20710     // private
20711     expandMenu : function(autoActivate){
20712         if(!this.disabled && this.menu){
20713             clearTimeout(this.hideTimer);
20714             delete this.hideTimer;
20715             if(!this.menu.isVisible() && !this.showTimer){
20716                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20717             }else if (this.menu.isVisible() && autoActivate){
20718                 this.menu.tryActivate(0, 1);
20719             }
20720         }
20721     },
20722
20723     // private
20724     deferExpand : function(autoActivate){
20725         delete this.showTimer;
20726         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20727         if(autoActivate){
20728             this.menu.tryActivate(0, 1);
20729         }
20730     },
20731
20732     // private
20733     hideMenu : function(){
20734         clearTimeout(this.showTimer);
20735         delete this.showTimer;
20736         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20737             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20738         }
20739     },
20740
20741     // private
20742     deferHide : function(){
20743         delete this.hideTimer;
20744         this.menu.hide();
20745     }
20746 });/*
20747  * Based on:
20748  * Ext JS Library 1.1.1
20749  * Copyright(c) 2006-2007, Ext JS, LLC.
20750  *
20751  * Originally Released Under LGPL - original licence link has changed is not relivant.
20752  *
20753  * Fork - LGPL
20754  * <script type="text/javascript">
20755  */
20756  
20757 /**
20758  * @class Roo.menu.CheckItem
20759  * @extends Roo.menu.Item
20760  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20761  * @constructor
20762  * Creates a new CheckItem
20763  * @param {Object} config Configuration options
20764  */
20765 Roo.menu.CheckItem = function(config){
20766     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20767     this.addEvents({
20768         /**
20769          * @event beforecheckchange
20770          * Fires before the checked value is set, providing an opportunity to cancel if needed
20771          * @param {Roo.menu.CheckItem} this
20772          * @param {Boolean} checked The new checked value that will be set
20773          */
20774         "beforecheckchange" : true,
20775         /**
20776          * @event checkchange
20777          * Fires after the checked value has been set
20778          * @param {Roo.menu.CheckItem} this
20779          * @param {Boolean} checked The checked value that was set
20780          */
20781         "checkchange" : true
20782     });
20783     if(this.checkHandler){
20784         this.on('checkchange', this.checkHandler, this.scope);
20785     }
20786 };
20787 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20788     /**
20789      * @cfg {String} group
20790      * All check items with the same group name will automatically be grouped into a single-select
20791      * radio button group (defaults to '')
20792      */
20793     /**
20794      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20795      */
20796     itemCls : "x-menu-item x-menu-check-item",
20797     /**
20798      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20799      */
20800     groupClass : "x-menu-group-item",
20801
20802     /**
20803      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20804      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20805      * initialized with checked = true will be rendered as checked.
20806      */
20807     checked: false,
20808
20809     // private
20810     ctype: "Roo.menu.CheckItem",
20811
20812     // private
20813     onRender : function(c){
20814         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20815         if(this.group){
20816             this.el.addClass(this.groupClass);
20817         }
20818         Roo.menu.MenuMgr.registerCheckable(this);
20819         if(this.checked){
20820             this.checked = false;
20821             this.setChecked(true, true);
20822         }
20823     },
20824
20825     // private
20826     destroy : function(){
20827         if(this.rendered){
20828             Roo.menu.MenuMgr.unregisterCheckable(this);
20829         }
20830         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20831     },
20832
20833     /**
20834      * Set the checked state of this item
20835      * @param {Boolean} checked The new checked value
20836      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20837      */
20838     setChecked : function(state, suppressEvent){
20839         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20840             if(this.container){
20841                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20842             }
20843             this.checked = state;
20844             if(suppressEvent !== true){
20845                 this.fireEvent("checkchange", this, state);
20846             }
20847         }
20848     },
20849
20850     // private
20851     handleClick : function(e){
20852        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20853            this.setChecked(!this.checked);
20854        }
20855        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20856     }
20857 });/*
20858  * Based on:
20859  * Ext JS Library 1.1.1
20860  * Copyright(c) 2006-2007, Ext JS, LLC.
20861  *
20862  * Originally Released Under LGPL - original licence link has changed is not relivant.
20863  *
20864  * Fork - LGPL
20865  * <script type="text/javascript">
20866  */
20867  
20868 /**
20869  * @class Roo.menu.DateItem
20870  * @extends Roo.menu.Adapter
20871  * A menu item that wraps the {@link Roo.DatPicker} component.
20872  * @constructor
20873  * Creates a new DateItem
20874  * @param {Object} config Configuration options
20875  */
20876 Roo.menu.DateItem = function(config){
20877     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20878     /** The Roo.DatePicker object @type Roo.DatePicker */
20879     this.picker = this.component;
20880     this.addEvents({select: true});
20881     
20882     this.picker.on("render", function(picker){
20883         picker.getEl().swallowEvent("click");
20884         picker.container.addClass("x-menu-date-item");
20885     });
20886
20887     this.picker.on("select", this.onSelect, this);
20888 };
20889
20890 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20891     // private
20892     onSelect : function(picker, date){
20893         this.fireEvent("select", this, date, picker);
20894         Roo.menu.DateItem.superclass.handleClick.call(this);
20895     }
20896 });/*
20897  * Based on:
20898  * Ext JS Library 1.1.1
20899  * Copyright(c) 2006-2007, Ext JS, LLC.
20900  *
20901  * Originally Released Under LGPL - original licence link has changed is not relivant.
20902  *
20903  * Fork - LGPL
20904  * <script type="text/javascript">
20905  */
20906  
20907 /**
20908  * @class Roo.menu.ColorItem
20909  * @extends Roo.menu.Adapter
20910  * A menu item that wraps the {@link Roo.ColorPalette} component.
20911  * @constructor
20912  * Creates a new ColorItem
20913  * @param {Object} config Configuration options
20914  */
20915 Roo.menu.ColorItem = function(config){
20916     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20917     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20918     this.palette = this.component;
20919     this.relayEvents(this.palette, ["select"]);
20920     if(this.selectHandler){
20921         this.on('select', this.selectHandler, this.scope);
20922     }
20923 };
20924 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20925  * Based on:
20926  * Ext JS Library 1.1.1
20927  * Copyright(c) 2006-2007, Ext JS, LLC.
20928  *
20929  * Originally Released Under LGPL - original licence link has changed is not relivant.
20930  *
20931  * Fork - LGPL
20932  * <script type="text/javascript">
20933  */
20934  
20935
20936 /**
20937  * @class Roo.menu.DateMenu
20938  * @extends Roo.menu.Menu
20939  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20940  * @constructor
20941  * Creates a new DateMenu
20942  * @param {Object} config Configuration options
20943  */
20944 Roo.menu.DateMenu = function(config){
20945     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20946     this.plain = true;
20947     var di = new Roo.menu.DateItem(config);
20948     this.add(di);
20949     /**
20950      * The {@link Roo.DatePicker} instance for this DateMenu
20951      * @type DatePicker
20952      */
20953     this.picker = di.picker;
20954     /**
20955      * @event select
20956      * @param {DatePicker} picker
20957      * @param {Date} date
20958      */
20959     this.relayEvents(di, ["select"]);
20960
20961     this.on('beforeshow', function(){
20962         if(this.picker){
20963             this.picker.hideMonthPicker(true);
20964         }
20965     }, this);
20966 };
20967 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20968     cls:'x-date-menu'
20969 });/*
20970  * Based on:
20971  * Ext JS Library 1.1.1
20972  * Copyright(c) 2006-2007, Ext JS, LLC.
20973  *
20974  * Originally Released Under LGPL - original licence link has changed is not relivant.
20975  *
20976  * Fork - LGPL
20977  * <script type="text/javascript">
20978  */
20979  
20980
20981 /**
20982  * @class Roo.menu.ColorMenu
20983  * @extends Roo.menu.Menu
20984  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20985  * @constructor
20986  * Creates a new ColorMenu
20987  * @param {Object} config Configuration options
20988  */
20989 Roo.menu.ColorMenu = function(config){
20990     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20991     this.plain = true;
20992     var ci = new Roo.menu.ColorItem(config);
20993     this.add(ci);
20994     /**
20995      * The {@link Roo.ColorPalette} instance for this ColorMenu
20996      * @type ColorPalette
20997      */
20998     this.palette = ci.palette;
20999     /**
21000      * @event select
21001      * @param {ColorPalette} palette
21002      * @param {String} color
21003      */
21004     this.relayEvents(ci, ["select"]);
21005 };
21006 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21007  * Based on:
21008  * Ext JS Library 1.1.1
21009  * Copyright(c) 2006-2007, Ext JS, LLC.
21010  *
21011  * Originally Released Under LGPL - original licence link has changed is not relivant.
21012  *
21013  * Fork - LGPL
21014  * <script type="text/javascript">
21015  */
21016  
21017 /**
21018  * @class Roo.form.Field
21019  * @extends Roo.BoxComponent
21020  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21021  * @constructor
21022  * Creates a new Field
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.form.Field = function(config){
21026     Roo.form.Field.superclass.constructor.call(this, config);
21027 };
21028
21029 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21030     /**
21031      * @cfg {String} fieldLabel Label to use when rendering a form.
21032      */
21033        /**
21034      * @cfg {String} qtip Mouse over tip
21035      */
21036      
21037     /**
21038      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21039      */
21040     invalidClass : "x-form-invalid",
21041     /**
21042      * @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")
21043      */
21044     invalidText : "The value in this field is invalid",
21045     /**
21046      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21047      */
21048     focusClass : "x-form-focus",
21049     /**
21050      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21051       automatic validation (defaults to "keyup").
21052      */
21053     validationEvent : "keyup",
21054     /**
21055      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21056      */
21057     validateOnBlur : true,
21058     /**
21059      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21060      */
21061     validationDelay : 250,
21062     /**
21063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21064      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21065      */
21066     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21067     /**
21068      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21069      */
21070     fieldClass : "x-form-field",
21071     /**
21072      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21073      *<pre>
21074 Value         Description
21075 -----------   ----------------------------------------------------------------------
21076 qtip          Display a quick tip when the user hovers over the field
21077 title         Display a default browser title attribute popup
21078 under         Add a block div beneath the field containing the error text
21079 side          Add an error icon to the right of the field with a popup on hover
21080 [element id]  Add the error text directly to the innerHTML of the specified element
21081 </pre>
21082      */
21083     msgTarget : 'qtip',
21084     /**
21085      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21086      */
21087     msgFx : 'normal',
21088
21089     /**
21090      * @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.
21091      */
21092     readOnly : false,
21093
21094     /**
21095      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21096      */
21097     disabled : false,
21098
21099     /**
21100      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21101      */
21102     inputType : undefined,
21103     
21104     /**
21105      * @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).
21106          */
21107         tabIndex : undefined,
21108         
21109     // private
21110     isFormField : true,
21111
21112     // private
21113     hasFocus : false,
21114     /**
21115      * @property {Roo.Element} fieldEl
21116      * Element Containing the rendered Field (with label etc.)
21117      */
21118     /**
21119      * @cfg {Mixed} value A value to initialize this field with.
21120      */
21121     value : undefined,
21122
21123     /**
21124      * @cfg {String} name The field's HTML name attribute.
21125      */
21126     /**
21127      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21128      */
21129
21130         // private ??
21131         initComponent : function(){
21132         Roo.form.Field.superclass.initComponent.call(this);
21133         this.addEvents({
21134             /**
21135              * @event focus
21136              * Fires when this field receives input focus.
21137              * @param {Roo.form.Field} this
21138              */
21139             focus : true,
21140             /**
21141              * @event blur
21142              * Fires when this field loses input focus.
21143              * @param {Roo.form.Field} this
21144              */
21145             blur : true,
21146             /**
21147              * @event specialkey
21148              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21149              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21150              * @param {Roo.form.Field} this
21151              * @param {Roo.EventObject} e The event object
21152              */
21153             specialkey : true,
21154             /**
21155              * @event change
21156              * Fires just before the field blurs if the field value has changed.
21157              * @param {Roo.form.Field} this
21158              * @param {Mixed} newValue The new value
21159              * @param {Mixed} oldValue The original value
21160              */
21161             change : true,
21162             /**
21163              * @event invalid
21164              * Fires after the field has been marked as invalid.
21165              * @param {Roo.form.Field} this
21166              * @param {String} msg The validation message
21167              */
21168             invalid : true,
21169             /**
21170              * @event valid
21171              * Fires after the field has been validated with no errors.
21172              * @param {Roo.form.Field} this
21173              */
21174             valid : true,
21175              /**
21176              * @event keyup
21177              * Fires after the key up
21178              * @param {Roo.form.Field} this
21179              * @param {Roo.EventObject}  e The event Object
21180              */
21181             keyup : true
21182         });
21183     },
21184
21185     /**
21186      * Returns the name attribute of the field if available
21187      * @return {String} name The field name
21188      */
21189     getName: function(){
21190          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21191     },
21192
21193     // private
21194     onRender : function(ct, position){
21195         Roo.form.Field.superclass.onRender.call(this, ct, position);
21196         if(!this.el){
21197             var cfg = this.getAutoCreate();
21198             if(!cfg.name){
21199                 cfg.name = this.name || this.id;
21200             }
21201             if(this.inputType){
21202                 cfg.type = this.inputType;
21203             }
21204             this.el = ct.createChild(cfg, position);
21205         }
21206         var type = this.el.dom.type;
21207         if(type){
21208             if(type == 'password'){
21209                 type = 'text';
21210             }
21211             this.el.addClass('x-form-'+type);
21212         }
21213         if(this.readOnly){
21214             this.el.dom.readOnly = true;
21215         }
21216         if(this.tabIndex !== undefined){
21217             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21218         }
21219
21220         this.el.addClass([this.fieldClass, this.cls]);
21221         this.initValue();
21222     },
21223
21224     /**
21225      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21226      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21227      * @return {Roo.form.Field} this
21228      */
21229     applyTo : function(target){
21230         this.allowDomMove = false;
21231         this.el = Roo.get(target);
21232         this.render(this.el.dom.parentNode);
21233         return this;
21234     },
21235
21236     // private
21237     initValue : function(){
21238         if(this.value !== undefined){
21239             this.setValue(this.value);
21240         }else if(this.el.dom.value.length > 0){
21241             this.setValue(this.el.dom.value);
21242         }
21243     },
21244
21245     /**
21246      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21247      */
21248     isDirty : function() {
21249         if(this.disabled) {
21250             return false;
21251         }
21252         return String(this.getValue()) !== String(this.originalValue);
21253     },
21254
21255     // private
21256     afterRender : function(){
21257         Roo.form.Field.superclass.afterRender.call(this);
21258         this.initEvents();
21259     },
21260
21261     // private
21262     fireKey : function(e){
21263         //Roo.log('field ' + e.getKey());
21264         if(e.isNavKeyPress()){
21265             this.fireEvent("specialkey", this, e);
21266         }
21267     },
21268
21269     /**
21270      * Resets the current field value to the originally loaded value and clears any validation messages
21271      */
21272     reset : function(){
21273         this.setValue(this.originalValue);
21274         this.clearInvalid();
21275     },
21276
21277     // private
21278     initEvents : function(){
21279         // safari killled keypress - so keydown is now used..
21280         this.el.on("keydown" , this.fireKey,  this);
21281         this.el.on("focus", this.onFocus,  this);
21282         this.el.on("blur", this.onBlur,  this);
21283         this.el.relayEvent('keyup', this);
21284
21285         // reference to original value for reset
21286         this.originalValue = this.getValue();
21287     },
21288
21289     // private
21290     onFocus : function(){
21291         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21292             this.el.addClass(this.focusClass);
21293         }
21294         if(!this.hasFocus){
21295             this.hasFocus = true;
21296             this.startValue = this.getValue();
21297             this.fireEvent("focus", this);
21298         }
21299     },
21300
21301     beforeBlur : Roo.emptyFn,
21302
21303     // private
21304     onBlur : function(){
21305         this.beforeBlur();
21306         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21307             this.el.removeClass(this.focusClass);
21308         }
21309         this.hasFocus = false;
21310         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21311             this.validate();
21312         }
21313         var v = this.getValue();
21314         if(String(v) !== String(this.startValue)){
21315             this.fireEvent('change', this, v, this.startValue);
21316         }
21317         this.fireEvent("blur", this);
21318     },
21319
21320     /**
21321      * Returns whether or not the field value is currently valid
21322      * @param {Boolean} preventMark True to disable marking the field invalid
21323      * @return {Boolean} True if the value is valid, else false
21324      */
21325     isValid : function(preventMark){
21326         if(this.disabled){
21327             return true;
21328         }
21329         var restore = this.preventMark;
21330         this.preventMark = preventMark === true;
21331         var v = this.validateValue(this.processValue(this.getRawValue()));
21332         this.preventMark = restore;
21333         return v;
21334     },
21335
21336     /**
21337      * Validates the field value
21338      * @return {Boolean} True if the value is valid, else false
21339      */
21340     validate : function(){
21341         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21342             this.clearInvalid();
21343             return true;
21344         }
21345         return false;
21346     },
21347
21348     processValue : function(value){
21349         return value;
21350     },
21351
21352     // private
21353     // Subclasses should provide the validation implementation by overriding this
21354     validateValue : function(value){
21355         return true;
21356     },
21357
21358     /**
21359      * Mark this field as invalid
21360      * @param {String} msg The validation message
21361      */
21362     markInvalid : function(msg){
21363         if(!this.rendered || this.preventMark){ // not rendered
21364             return;
21365         }
21366         this.el.addClass(this.invalidClass);
21367         msg = msg || this.invalidText;
21368         switch(this.msgTarget){
21369             case 'qtip':
21370                 this.el.dom.qtip = msg;
21371                 this.el.dom.qclass = 'x-form-invalid-tip';
21372                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21373                     Roo.QuickTips.enable();
21374                 }
21375                 break;
21376             case 'title':
21377                 this.el.dom.title = msg;
21378                 break;
21379             case 'under':
21380                 if(!this.errorEl){
21381                     var elp = this.el.findParent('.x-form-element', 5, true);
21382                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21383                     this.errorEl.setWidth(elp.getWidth(true)-20);
21384                 }
21385                 this.errorEl.update(msg);
21386                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21387                 break;
21388             case 'side':
21389                 if(!this.errorIcon){
21390                     var elp = this.el.findParent('.x-form-element', 5, true);
21391                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21392                 }
21393                 this.alignErrorIcon();
21394                 this.errorIcon.dom.qtip = msg;
21395                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21396                 this.errorIcon.show();
21397                 this.on('resize', this.alignErrorIcon, this);
21398                 break;
21399             default:
21400                 var t = Roo.getDom(this.msgTarget);
21401                 t.innerHTML = msg;
21402                 t.style.display = this.msgDisplay;
21403                 break;
21404         }
21405         this.fireEvent('invalid', this, msg);
21406     },
21407
21408     // private
21409     alignErrorIcon : function(){
21410         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21411     },
21412
21413     /**
21414      * Clear any invalid styles/messages for this field
21415      */
21416     clearInvalid : function(){
21417         if(!this.rendered || this.preventMark){ // not rendered
21418             return;
21419         }
21420         this.el.removeClass(this.invalidClass);
21421         switch(this.msgTarget){
21422             case 'qtip':
21423                 this.el.dom.qtip = '';
21424                 break;
21425             case 'title':
21426                 this.el.dom.title = '';
21427                 break;
21428             case 'under':
21429                 if(this.errorEl){
21430                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21431                 }
21432                 break;
21433             case 'side':
21434                 if(this.errorIcon){
21435                     this.errorIcon.dom.qtip = '';
21436                     this.errorIcon.hide();
21437                     this.un('resize', this.alignErrorIcon, this);
21438                 }
21439                 break;
21440             default:
21441                 var t = Roo.getDom(this.msgTarget);
21442                 t.innerHTML = '';
21443                 t.style.display = 'none';
21444                 break;
21445         }
21446         this.fireEvent('valid', this);
21447     },
21448
21449     /**
21450      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21451      * @return {Mixed} value The field value
21452      */
21453     getRawValue : function(){
21454         var v = this.el.getValue();
21455         if(v === this.emptyText){
21456             v = '';
21457         }
21458         return v;
21459     },
21460
21461     /**
21462      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21463      * @return {Mixed} value The field value
21464      */
21465     getValue : function(){
21466         var v = this.el.getValue();
21467         if(v === this.emptyText || v === undefined){
21468             v = '';
21469         }
21470         return v;
21471     },
21472
21473     /**
21474      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21475      * @param {Mixed} value The value to set
21476      */
21477     setRawValue : function(v){
21478         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21479     },
21480
21481     /**
21482      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21483      * @param {Mixed} value The value to set
21484      */
21485     setValue : function(v){
21486         this.value = v;
21487         if(this.rendered){
21488             this.el.dom.value = (v === null || v === undefined ? '' : v);
21489              this.validate();
21490         }
21491     },
21492
21493     adjustSize : function(w, h){
21494         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21495         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21496         return s;
21497     },
21498
21499     adjustWidth : function(tag, w){
21500         tag = tag.toLowerCase();
21501         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21502             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21503                 if(tag == 'input'){
21504                     return w + 2;
21505                 }
21506                 if(tag = 'textarea'){
21507                     return w-2;
21508                 }
21509             }else if(Roo.isOpera){
21510                 if(tag == 'input'){
21511                     return w + 2;
21512                 }
21513                 if(tag = 'textarea'){
21514                     return w-2;
21515                 }
21516             }
21517         }
21518         return w;
21519     }
21520 });
21521
21522
21523 // anything other than normal should be considered experimental
21524 Roo.form.Field.msgFx = {
21525     normal : {
21526         show: function(msgEl, f){
21527             msgEl.setDisplayed('block');
21528         },
21529
21530         hide : function(msgEl, f){
21531             msgEl.setDisplayed(false).update('');
21532         }
21533     },
21534
21535     slide : {
21536         show: function(msgEl, f){
21537             msgEl.slideIn('t', {stopFx:true});
21538         },
21539
21540         hide : function(msgEl, f){
21541             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21542         }
21543     },
21544
21545     slideRight : {
21546         show: function(msgEl, f){
21547             msgEl.fixDisplay();
21548             msgEl.alignTo(f.el, 'tl-tr');
21549             msgEl.slideIn('l', {stopFx:true});
21550         },
21551
21552         hide : function(msgEl, f){
21553             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21554         }
21555     }
21556 };/*
21557  * Based on:
21558  * Ext JS Library 1.1.1
21559  * Copyright(c) 2006-2007, Ext JS, LLC.
21560  *
21561  * Originally Released Under LGPL - original licence link has changed is not relivant.
21562  *
21563  * Fork - LGPL
21564  * <script type="text/javascript">
21565  */
21566  
21567
21568 /**
21569  * @class Roo.form.TextField
21570  * @extends Roo.form.Field
21571  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21572  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21573  * @constructor
21574  * Creates a new TextField
21575  * @param {Object} config Configuration options
21576  */
21577 Roo.form.TextField = function(config){
21578     Roo.form.TextField.superclass.constructor.call(this, config);
21579     this.addEvents({
21580         /**
21581          * @event autosize
21582          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21583          * according to the default logic, but this event provides a hook for the developer to apply additional
21584          * logic at runtime to resize the field if needed.
21585              * @param {Roo.form.Field} this This text field
21586              * @param {Number} width The new field width
21587              */
21588         autosize : true
21589     });
21590 };
21591
21592 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21593     /**
21594      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21595      */
21596     grow : false,
21597     /**
21598      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21599      */
21600     growMin : 30,
21601     /**
21602      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21603      */
21604     growMax : 800,
21605     /**
21606      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21607      */
21608     vtype : null,
21609     /**
21610      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21611      */
21612     maskRe : null,
21613     /**
21614      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21615      */
21616     disableKeyFilter : false,
21617     /**
21618      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21619      */
21620     allowBlank : true,
21621     /**
21622      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21623      */
21624     minLength : 0,
21625     /**
21626      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21627      */
21628     maxLength : Number.MAX_VALUE,
21629     /**
21630      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21631      */
21632     minLengthText : "The minimum length for this field is {0}",
21633     /**
21634      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21635      */
21636     maxLengthText : "The maximum length for this field is {0}",
21637     /**
21638      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21639      */
21640     selectOnFocus : false,
21641     /**
21642      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21643      */
21644     blankText : "This field is required",
21645     /**
21646      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21647      * If available, this function will be called only after the basic validators all return true, and will be passed the
21648      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21649      */
21650     validator : null,
21651     /**
21652      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21653      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21654      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21655      */
21656     regex : null,
21657     /**
21658      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21659      */
21660     regexText : "",
21661     /**
21662      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21663      */
21664     emptyText : null,
21665     /**
21666      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21667      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21668      */
21669     emptyClass : 'x-form-empty-field',
21670
21671     // private
21672     initEvents : function(){
21673         Roo.form.TextField.superclass.initEvents.call(this);
21674         if(this.validationEvent == 'keyup'){
21675             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21676             this.el.on('keyup', this.filterValidation, this);
21677         }
21678         else if(this.validationEvent !== false){
21679             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21680         }
21681         if(this.selectOnFocus || this.emptyText){
21682             this.on("focus", this.preFocus, this);
21683             if(this.emptyText){
21684                 this.on('blur', this.postBlur, this);
21685                 this.applyEmptyText();
21686             }
21687         }
21688         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21689             this.el.on("keypress", this.filterKeys, this);
21690         }
21691         if(this.grow){
21692             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21693             this.el.on("click", this.autoSize,  this);
21694         }
21695     },
21696
21697     processValue : function(value){
21698         if(this.stripCharsRe){
21699             var newValue = value.replace(this.stripCharsRe, '');
21700             if(newValue !== value){
21701                 this.setRawValue(newValue);
21702                 return newValue;
21703             }
21704         }
21705         return value;
21706     },
21707
21708     filterValidation : function(e){
21709         if(!e.isNavKeyPress()){
21710             this.validationTask.delay(this.validationDelay);
21711         }
21712     },
21713
21714     // private
21715     onKeyUp : function(e){
21716         if(!e.isNavKeyPress()){
21717             this.autoSize();
21718         }
21719     },
21720
21721     /**
21722      * Resets the current field value to the originally-loaded value and clears any validation messages.
21723      * Also adds emptyText and emptyClass if the original value was blank.
21724      */
21725     reset : function(){
21726         Roo.form.TextField.superclass.reset.call(this);
21727         this.applyEmptyText();
21728     },
21729
21730     applyEmptyText : function(){
21731         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21732             this.setRawValue(this.emptyText);
21733             this.el.addClass(this.emptyClass);
21734         }
21735     },
21736
21737     // private
21738     preFocus : function(){
21739         if(this.emptyText){
21740             if(this.el.dom.value == this.emptyText){
21741                 this.setRawValue('');
21742             }
21743             this.el.removeClass(this.emptyClass);
21744         }
21745         if(this.selectOnFocus){
21746             this.el.dom.select();
21747         }
21748     },
21749
21750     // private
21751     postBlur : function(){
21752         this.applyEmptyText();
21753     },
21754
21755     // private
21756     filterKeys : function(e){
21757         var k = e.getKey();
21758         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21759             return;
21760         }
21761         var c = e.getCharCode(), cc = String.fromCharCode(c);
21762         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21763             return;
21764         }
21765         if(!this.maskRe.test(cc)){
21766             e.stopEvent();
21767         }
21768     },
21769
21770     setValue : function(v){
21771         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21772             this.el.removeClass(this.emptyClass);
21773         }
21774         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21775         this.applyEmptyText();
21776         this.autoSize();
21777     },
21778
21779     /**
21780      * Validates a value according to the field's validation rules and marks the field as invalid
21781      * if the validation fails
21782      * @param {Mixed} value The value to validate
21783      * @return {Boolean} True if the value is valid, else false
21784      */
21785     validateValue : function(value){
21786         if(value.length < 1 || value === this.emptyText){ // if it's blank
21787              if(this.allowBlank){
21788                 this.clearInvalid();
21789                 return true;
21790              }else{
21791                 this.markInvalid(this.blankText);
21792                 return false;
21793              }
21794         }
21795         if(value.length < this.minLength){
21796             this.markInvalid(String.format(this.minLengthText, this.minLength));
21797             return false;
21798         }
21799         if(value.length > this.maxLength){
21800             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21801             return false;
21802         }
21803         if(this.vtype){
21804             var vt = Roo.form.VTypes;
21805             if(!vt[this.vtype](value, this)){
21806                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21807                 return false;
21808             }
21809         }
21810         if(typeof this.validator == "function"){
21811             var msg = this.validator(value);
21812             if(msg !== true){
21813                 this.markInvalid(msg);
21814                 return false;
21815             }
21816         }
21817         if(this.regex && !this.regex.test(value)){
21818             this.markInvalid(this.regexText);
21819             return false;
21820         }
21821         return true;
21822     },
21823
21824     /**
21825      * Selects text in this field
21826      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21827      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21828      */
21829     selectText : function(start, end){
21830         var v = this.getRawValue();
21831         if(v.length > 0){
21832             start = start === undefined ? 0 : start;
21833             end = end === undefined ? v.length : end;
21834             var d = this.el.dom;
21835             if(d.setSelectionRange){
21836                 d.setSelectionRange(start, end);
21837             }else if(d.createTextRange){
21838                 var range = d.createTextRange();
21839                 range.moveStart("character", start);
21840                 range.moveEnd("character", v.length-end);
21841                 range.select();
21842             }
21843         }
21844     },
21845
21846     /**
21847      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21848      * This only takes effect if grow = true, and fires the autosize event.
21849      */
21850     autoSize : function(){
21851         if(!this.grow || !this.rendered){
21852             return;
21853         }
21854         if(!this.metrics){
21855             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21856         }
21857         var el = this.el;
21858         var v = el.dom.value;
21859         var d = document.createElement('div');
21860         d.appendChild(document.createTextNode(v));
21861         v = d.innerHTML;
21862         d = null;
21863         v += "&#160;";
21864         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21865         this.el.setWidth(w);
21866         this.fireEvent("autosize", this, w);
21867     }
21868 });/*
21869  * Based on:
21870  * Ext JS Library 1.1.1
21871  * Copyright(c) 2006-2007, Ext JS, LLC.
21872  *
21873  * Originally Released Under LGPL - original licence link has changed is not relivant.
21874  *
21875  * Fork - LGPL
21876  * <script type="text/javascript">
21877  */
21878  
21879 /**
21880  * @class Roo.form.Hidden
21881  * @extends Roo.form.TextField
21882  * Simple Hidden element used on forms 
21883  * 
21884  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21885  * 
21886  * @constructor
21887  * Creates a new Hidden form element.
21888  * @param {Object} config Configuration options
21889  */
21890
21891
21892
21893 // easy hidden field...
21894 Roo.form.Hidden = function(config){
21895     Roo.form.Hidden.superclass.constructor.call(this, config);
21896 };
21897   
21898 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21899     fieldLabel:      '',
21900     inputType:      'hidden',
21901     width:          50,
21902     allowBlank:     true,
21903     labelSeparator: '',
21904     hidden:         true,
21905     itemCls :       'x-form-item-display-none'
21906
21907
21908 });
21909
21910
21911 /*
21912  * Based on:
21913  * Ext JS Library 1.1.1
21914  * Copyright(c) 2006-2007, Ext JS, LLC.
21915  *
21916  * Originally Released Under LGPL - original licence link has changed is not relivant.
21917  *
21918  * Fork - LGPL
21919  * <script type="text/javascript">
21920  */
21921  
21922 /**
21923  * @class Roo.form.TriggerField
21924  * @extends Roo.form.TextField
21925  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21926  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21927  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21928  * for which you can provide a custom implementation.  For example:
21929  * <pre><code>
21930 var trigger = new Roo.form.TriggerField();
21931 trigger.onTriggerClick = myTriggerFn;
21932 trigger.applyTo('my-field');
21933 </code></pre>
21934  *
21935  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21936  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21937  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21938  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21939  * @constructor
21940  * Create a new TriggerField.
21941  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21942  * to the base TextField)
21943  */
21944 Roo.form.TriggerField = function(config){
21945     this.mimicing = false;
21946     Roo.form.TriggerField.superclass.constructor.call(this, config);
21947 };
21948
21949 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21950     /**
21951      * @cfg {String} triggerClass A CSS class to apply to the trigger
21952      */
21953     /**
21954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21955      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21956      */
21957     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21958     /**
21959      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21960      */
21961     hideTrigger:false,
21962
21963     /** @cfg {Boolean} grow @hide */
21964     /** @cfg {Number} growMin @hide */
21965     /** @cfg {Number} growMax @hide */
21966
21967     /**
21968      * @hide 
21969      * @method
21970      */
21971     autoSize: Roo.emptyFn,
21972     // private
21973     monitorTab : true,
21974     // private
21975     deferHeight : true,
21976
21977     
21978     actionMode : 'wrap',
21979     // private
21980     onResize : function(w, h){
21981         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21982         if(typeof w == 'number'){
21983             var x = w - this.trigger.getWidth();
21984             this.el.setWidth(this.adjustWidth('input', x));
21985             this.trigger.setStyle('left', x+'px');
21986         }
21987     },
21988
21989     // private
21990     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21991
21992     // private
21993     getResizeEl : function(){
21994         return this.wrap;
21995     },
21996
21997     // private
21998     getPositionEl : function(){
21999         return this.wrap;
22000     },
22001
22002     // private
22003     alignErrorIcon : function(){
22004         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22005     },
22006
22007     // private
22008     onRender : function(ct, position){
22009         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22010         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22011         this.trigger = this.wrap.createChild(this.triggerConfig ||
22012                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22013         if(this.hideTrigger){
22014             this.trigger.setDisplayed(false);
22015         }
22016         this.initTrigger();
22017         if(!this.width){
22018             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22019         }
22020     },
22021
22022     // private
22023     initTrigger : function(){
22024         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22025         this.trigger.addClassOnOver('x-form-trigger-over');
22026         this.trigger.addClassOnClick('x-form-trigger-click');
22027     },
22028
22029     // private
22030     onDestroy : function(){
22031         if(this.trigger){
22032             this.trigger.removeAllListeners();
22033             this.trigger.remove();
22034         }
22035         if(this.wrap){
22036             this.wrap.remove();
22037         }
22038         Roo.form.TriggerField.superclass.onDestroy.call(this);
22039     },
22040
22041     // private
22042     onFocus : function(){
22043         Roo.form.TriggerField.superclass.onFocus.call(this);
22044         if(!this.mimicing){
22045             this.wrap.addClass('x-trigger-wrap-focus');
22046             this.mimicing = true;
22047             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22048             if(this.monitorTab){
22049                 this.el.on("keydown", this.checkTab, this);
22050             }
22051         }
22052     },
22053
22054     // private
22055     checkTab : function(e){
22056         if(e.getKey() == e.TAB){
22057             this.triggerBlur();
22058         }
22059     },
22060
22061     // private
22062     onBlur : function(){
22063         // do nothing
22064     },
22065
22066     // private
22067     mimicBlur : function(e, t){
22068         if(!this.wrap.contains(t) && this.validateBlur()){
22069             this.triggerBlur();
22070         }
22071     },
22072
22073     // private
22074     triggerBlur : function(){
22075         this.mimicing = false;
22076         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22077         if(this.monitorTab){
22078             this.el.un("keydown", this.checkTab, this);
22079         }
22080         this.wrap.removeClass('x-trigger-wrap-focus');
22081         Roo.form.TriggerField.superclass.onBlur.call(this);
22082     },
22083
22084     // private
22085     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22086     validateBlur : function(e, t){
22087         return true;
22088     },
22089
22090     // private
22091     onDisable : function(){
22092         Roo.form.TriggerField.superclass.onDisable.call(this);
22093         if(this.wrap){
22094             this.wrap.addClass('x-item-disabled');
22095         }
22096     },
22097
22098     // private
22099     onEnable : function(){
22100         Roo.form.TriggerField.superclass.onEnable.call(this);
22101         if(this.wrap){
22102             this.wrap.removeClass('x-item-disabled');
22103         }
22104     },
22105
22106     // private
22107     onShow : function(){
22108         var ae = this.getActionEl();
22109         
22110         if(ae){
22111             ae.dom.style.display = '';
22112             ae.dom.style.visibility = 'visible';
22113         }
22114     },
22115
22116     // private
22117     
22118     onHide : function(){
22119         var ae = this.getActionEl();
22120         ae.dom.style.display = 'none';
22121     },
22122
22123     /**
22124      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22125      * by an implementing function.
22126      * @method
22127      * @param {EventObject} e
22128      */
22129     onTriggerClick : Roo.emptyFn
22130 });
22131
22132 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22133 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22134 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22135 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22136     initComponent : function(){
22137         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22138
22139         this.triggerConfig = {
22140             tag:'span', cls:'x-form-twin-triggers', cn:[
22141             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22142             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22143         ]};
22144     },
22145
22146     getTrigger : function(index){
22147         return this.triggers[index];
22148     },
22149
22150     initTrigger : function(){
22151         var ts = this.trigger.select('.x-form-trigger', true);
22152         this.wrap.setStyle('overflow', 'hidden');
22153         var triggerField = this;
22154         ts.each(function(t, all, index){
22155             t.hide = function(){
22156                 var w = triggerField.wrap.getWidth();
22157                 this.dom.style.display = 'none';
22158                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22159             };
22160             t.show = function(){
22161                 var w = triggerField.wrap.getWidth();
22162                 this.dom.style.display = '';
22163                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22164             };
22165             var triggerIndex = 'Trigger'+(index+1);
22166
22167             if(this['hide'+triggerIndex]){
22168                 t.dom.style.display = 'none';
22169             }
22170             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22171             t.addClassOnOver('x-form-trigger-over');
22172             t.addClassOnClick('x-form-trigger-click');
22173         }, this);
22174         this.triggers = ts.elements;
22175     },
22176
22177     onTrigger1Click : Roo.emptyFn,
22178     onTrigger2Click : Roo.emptyFn
22179 });/*
22180  * Based on:
22181  * Ext JS Library 1.1.1
22182  * Copyright(c) 2006-2007, Ext JS, LLC.
22183  *
22184  * Originally Released Under LGPL - original licence link has changed is not relivant.
22185  *
22186  * Fork - LGPL
22187  * <script type="text/javascript">
22188  */
22189  
22190 /**
22191  * @class Roo.form.TextArea
22192  * @extends Roo.form.TextField
22193  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22194  * support for auto-sizing.
22195  * @constructor
22196  * Creates a new TextArea
22197  * @param {Object} config Configuration options
22198  */
22199 Roo.form.TextArea = function(config){
22200     Roo.form.TextArea.superclass.constructor.call(this, config);
22201     // these are provided exchanges for backwards compat
22202     // minHeight/maxHeight were replaced by growMin/growMax to be
22203     // compatible with TextField growing config values
22204     if(this.minHeight !== undefined){
22205         this.growMin = this.minHeight;
22206     }
22207     if(this.maxHeight !== undefined){
22208         this.growMax = this.maxHeight;
22209     }
22210 };
22211
22212 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22213     /**
22214      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22215      */
22216     growMin : 60,
22217     /**
22218      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22219      */
22220     growMax: 1000,
22221     /**
22222      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22223      * in the field (equivalent to setting overflow: hidden, defaults to false)
22224      */
22225     preventScrollbars: false,
22226     /**
22227      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22228      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22229      */
22230
22231     // private
22232     onRender : function(ct, position){
22233         if(!this.el){
22234             this.defaultAutoCreate = {
22235                 tag: "textarea",
22236                 style:"width:300px;height:60px;",
22237                 autocomplete: "off"
22238             };
22239         }
22240         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22241         if(this.grow){
22242             this.textSizeEl = Roo.DomHelper.append(document.body, {
22243                 tag: "pre", cls: "x-form-grow-sizer"
22244             });
22245             if(this.preventScrollbars){
22246                 this.el.setStyle("overflow", "hidden");
22247             }
22248             this.el.setHeight(this.growMin);
22249         }
22250     },
22251
22252     onDestroy : function(){
22253         if(this.textSizeEl){
22254             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22255         }
22256         Roo.form.TextArea.superclass.onDestroy.call(this);
22257     },
22258
22259     // private
22260     onKeyUp : function(e){
22261         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22262             this.autoSize();
22263         }
22264     },
22265
22266     /**
22267      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22268      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22269      */
22270     autoSize : function(){
22271         if(!this.grow || !this.textSizeEl){
22272             return;
22273         }
22274         var el = this.el;
22275         var v = el.dom.value;
22276         var ts = this.textSizeEl;
22277
22278         ts.innerHTML = '';
22279         ts.appendChild(document.createTextNode(v));
22280         v = ts.innerHTML;
22281
22282         Roo.fly(ts).setWidth(this.el.getWidth());
22283         if(v.length < 1){
22284             v = "&#160;&#160;";
22285         }else{
22286             if(Roo.isIE){
22287                 v = v.replace(/\n/g, '<p>&#160;</p>');
22288             }
22289             v += "&#160;\n&#160;";
22290         }
22291         ts.innerHTML = v;
22292         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22293         if(h != this.lastHeight){
22294             this.lastHeight = h;
22295             this.el.setHeight(h);
22296             this.fireEvent("autosize", this, h);
22297         }
22298     }
22299 });/*
22300  * Based on:
22301  * Ext JS Library 1.1.1
22302  * Copyright(c) 2006-2007, Ext JS, LLC.
22303  *
22304  * Originally Released Under LGPL - original licence link has changed is not relivant.
22305  *
22306  * Fork - LGPL
22307  * <script type="text/javascript">
22308  */
22309  
22310
22311 /**
22312  * @class Roo.form.NumberField
22313  * @extends Roo.form.TextField
22314  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22315  * @constructor
22316  * Creates a new NumberField
22317  * @param {Object} config Configuration options
22318  */
22319 Roo.form.NumberField = function(config){
22320     Roo.form.NumberField.superclass.constructor.call(this, config);
22321 };
22322
22323 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22324     /**
22325      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22326      */
22327     fieldClass: "x-form-field x-form-num-field",
22328     /**
22329      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22330      */
22331     allowDecimals : true,
22332     /**
22333      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22334      */
22335     decimalSeparator : ".",
22336     /**
22337      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22338      */
22339     decimalPrecision : 2,
22340     /**
22341      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22342      */
22343     allowNegative : true,
22344     /**
22345      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22346      */
22347     minValue : Number.NEGATIVE_INFINITY,
22348     /**
22349      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22350      */
22351     maxValue : Number.MAX_VALUE,
22352     /**
22353      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22354      */
22355     minText : "The minimum value for this field is {0}",
22356     /**
22357      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22358      */
22359     maxText : "The maximum value for this field is {0}",
22360     /**
22361      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22362      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22363      */
22364     nanText : "{0} is not a valid number",
22365
22366     // private
22367     initEvents : function(){
22368         Roo.form.NumberField.superclass.initEvents.call(this);
22369         var allowed = "0123456789";
22370         if(this.allowDecimals){
22371             allowed += this.decimalSeparator;
22372         }
22373         if(this.allowNegative){
22374             allowed += "-";
22375         }
22376         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22377         var keyPress = function(e){
22378             var k = e.getKey();
22379             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22380                 return;
22381             }
22382             var c = e.getCharCode();
22383             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22384                 e.stopEvent();
22385             }
22386         };
22387         this.el.on("keypress", keyPress, this);
22388     },
22389
22390     // private
22391     validateValue : function(value){
22392         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22393             return false;
22394         }
22395         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22396              return true;
22397         }
22398         var num = this.parseValue(value);
22399         if(isNaN(num)){
22400             this.markInvalid(String.format(this.nanText, value));
22401             return false;
22402         }
22403         if(num < this.minValue){
22404             this.markInvalid(String.format(this.minText, this.minValue));
22405             return false;
22406         }
22407         if(num > this.maxValue){
22408             this.markInvalid(String.format(this.maxText, this.maxValue));
22409             return false;
22410         }
22411         return true;
22412     },
22413
22414     getValue : function(){
22415         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22416     },
22417
22418     // private
22419     parseValue : function(value){
22420         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22421         return isNaN(value) ? '' : value;
22422     },
22423
22424     // private
22425     fixPrecision : function(value){
22426         var nan = isNaN(value);
22427         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22428             return nan ? '' : value;
22429         }
22430         return parseFloat(value).toFixed(this.decimalPrecision);
22431     },
22432
22433     setValue : function(v){
22434         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22435     },
22436
22437     // private
22438     decimalPrecisionFcn : function(v){
22439         return Math.floor(v);
22440     },
22441
22442     beforeBlur : function(){
22443         var v = this.parseValue(this.getRawValue());
22444         if(v){
22445             this.setValue(this.fixPrecision(v));
22446         }
22447     }
22448 });/*
22449  * Based on:
22450  * Ext JS Library 1.1.1
22451  * Copyright(c) 2006-2007, Ext JS, LLC.
22452  *
22453  * Originally Released Under LGPL - original licence link has changed is not relivant.
22454  *
22455  * Fork - LGPL
22456  * <script type="text/javascript">
22457  */
22458  
22459 /**
22460  * @class Roo.form.DateField
22461  * @extends Roo.form.TriggerField
22462  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22463 * @constructor
22464 * Create a new DateField
22465 * @param {Object} config
22466  */
22467 Roo.form.DateField = function(config){
22468     Roo.form.DateField.superclass.constructor.call(this, config);
22469     
22470       this.addEvents({
22471          
22472         /**
22473          * @event select
22474          * Fires when a date is selected
22475              * @param {Roo.form.DateField} combo This combo box
22476              * @param {Date} date The date selected
22477              */
22478         'select' : true
22479          
22480     });
22481     
22482     
22483     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22484     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22485     this.ddMatch = null;
22486     if(this.disabledDates){
22487         var dd = this.disabledDates;
22488         var re = "(?:";
22489         for(var i = 0; i < dd.length; i++){
22490             re += dd[i];
22491             if(i != dd.length-1) re += "|";
22492         }
22493         this.ddMatch = new RegExp(re + ")");
22494     }
22495 };
22496
22497 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22498     /**
22499      * @cfg {String} format
22500      * The default date format string which can be overriden for localization support.  The format must be
22501      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22502      */
22503     format : "m/d/y",
22504     /**
22505      * @cfg {String} altFormats
22506      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22507      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22508      */
22509     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22510     /**
22511      * @cfg {Array} disabledDays
22512      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22513      */
22514     disabledDays : null,
22515     /**
22516      * @cfg {String} disabledDaysText
22517      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22518      */
22519     disabledDaysText : "Disabled",
22520     /**
22521      * @cfg {Array} disabledDates
22522      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22523      * expression so they are very powerful. Some examples:
22524      * <ul>
22525      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22526      * <li>["03/08", "09/16"] would disable those days for every year</li>
22527      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22528      * <li>["03/../2006"] would disable every day in March 2006</li>
22529      * <li>["^03"] would disable every day in every March</li>
22530      * </ul>
22531      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22532      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22533      */
22534     disabledDates : null,
22535     /**
22536      * @cfg {String} disabledDatesText
22537      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22538      */
22539     disabledDatesText : "Disabled",
22540     /**
22541      * @cfg {Date/String} minValue
22542      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22543      * valid format (defaults to null).
22544      */
22545     minValue : null,
22546     /**
22547      * @cfg {Date/String} maxValue
22548      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22549      * valid format (defaults to null).
22550      */
22551     maxValue : null,
22552     /**
22553      * @cfg {String} minText
22554      * The error text to display when the date in the cell is before minValue (defaults to
22555      * 'The date in this field must be after {minValue}').
22556      */
22557     minText : "The date in this field must be equal to or after {0}",
22558     /**
22559      * @cfg {String} maxText
22560      * The error text to display when the date in the cell is after maxValue (defaults to
22561      * 'The date in this field must be before {maxValue}').
22562      */
22563     maxText : "The date in this field must be equal to or before {0}",
22564     /**
22565      * @cfg {String} invalidText
22566      * The error text to display when the date in the field is invalid (defaults to
22567      * '{value} is not a valid date - it must be in the format {format}').
22568      */
22569     invalidText : "{0} is not a valid date - it must be in the format {1}",
22570     /**
22571      * @cfg {String} triggerClass
22572      * An additional CSS class used to style the trigger button.  The trigger will always get the
22573      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22574      * which displays a calendar icon).
22575      */
22576     triggerClass : 'x-form-date-trigger',
22577     
22578
22579     /**
22580      * @cfg {bool} useIso
22581      * if enabled, then the date field will use a hidden field to store the 
22582      * real value as iso formated date. default (false)
22583      */ 
22584     useIso : false,
22585     /**
22586      * @cfg {String/Object} autoCreate
22587      * A DomHelper element spec, or true for a default element spec (defaults to
22588      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22589      */ 
22590     // private
22591     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22592     
22593     // private
22594     hiddenField: false,
22595     
22596     onRender : function(ct, position)
22597     {
22598         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22599         if (this.useIso) {
22600             this.el.dom.removeAttribute('name'); 
22601             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22602                     'before', true);
22603             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22604             // prevent input submission
22605             this.hiddenName = this.name;
22606         }
22607             
22608             
22609     },
22610     
22611     // private
22612     validateValue : function(value)
22613     {
22614         value = this.formatDate(value);
22615         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22616             return false;
22617         }
22618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22619              return true;
22620         }
22621         var svalue = value;
22622         value = this.parseDate(value);
22623         if(!value){
22624             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22625             return false;
22626         }
22627         var time = value.getTime();
22628         if(this.minValue && time < this.minValue.getTime()){
22629             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22630             return false;
22631         }
22632         if(this.maxValue && time > this.maxValue.getTime()){
22633             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22634             return false;
22635         }
22636         if(this.disabledDays){
22637             var day = value.getDay();
22638             for(var i = 0; i < this.disabledDays.length; i++) {
22639                 if(day === this.disabledDays[i]){
22640                     this.markInvalid(this.disabledDaysText);
22641                     return false;
22642                 }
22643             }
22644         }
22645         var fvalue = this.formatDate(value);
22646         if(this.ddMatch && this.ddMatch.test(fvalue)){
22647             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22648             return false;
22649         }
22650         return true;
22651     },
22652
22653     // private
22654     // Provides logic to override the default TriggerField.validateBlur which just returns true
22655     validateBlur : function(){
22656         return !this.menu || !this.menu.isVisible();
22657     },
22658
22659     /**
22660      * Returns the current date value of the date field.
22661      * @return {Date} The date value
22662      */
22663     getValue : function(){
22664         
22665         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22666     },
22667
22668     /**
22669      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22670      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22671      * (the default format used is "m/d/y").
22672      * <br />Usage:
22673      * <pre><code>
22674 //All of these calls set the same date value (May 4, 2006)
22675
22676 //Pass a date object:
22677 var dt = new Date('5/4/06');
22678 dateField.setValue(dt);
22679
22680 //Pass a date string (default format):
22681 dateField.setValue('5/4/06');
22682
22683 //Pass a date string (custom format):
22684 dateField.format = 'Y-m-d';
22685 dateField.setValue('2006-5-4');
22686 </code></pre>
22687      * @param {String/Date} date The date or valid date string
22688      */
22689     setValue : function(date){
22690         if (this.hiddenField) {
22691             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22692         }
22693         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22694     },
22695
22696     // private
22697     parseDate : function(value){
22698         if(!value || value instanceof Date){
22699             return value;
22700         }
22701         var v = Date.parseDate(value, this.format);
22702         if(!v && this.altFormats){
22703             if(!this.altFormatsArray){
22704                 this.altFormatsArray = this.altFormats.split("|");
22705             }
22706             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22707                 v = Date.parseDate(value, this.altFormatsArray[i]);
22708             }
22709         }
22710         return v;
22711     },
22712
22713     // private
22714     formatDate : function(date, fmt){
22715         return (!date || !(date instanceof Date)) ?
22716                date : date.dateFormat(fmt || this.format);
22717     },
22718
22719     // private
22720     menuListeners : {
22721         select: function(m, d){
22722             this.setValue(d);
22723             this.fireEvent('select', this, d);
22724         },
22725         show : function(){ // retain focus styling
22726             this.onFocus();
22727         },
22728         hide : function(){
22729             this.focus.defer(10, this);
22730             var ml = this.menuListeners;
22731             this.menu.un("select", ml.select,  this);
22732             this.menu.un("show", ml.show,  this);
22733             this.menu.un("hide", ml.hide,  this);
22734         }
22735     },
22736
22737     // private
22738     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22739     onTriggerClick : function(){
22740         if(this.disabled){
22741             return;
22742         }
22743         if(this.menu == null){
22744             this.menu = new Roo.menu.DateMenu();
22745         }
22746         Roo.apply(this.menu.picker,  {
22747             showClear: this.allowBlank,
22748             minDate : this.minValue,
22749             maxDate : this.maxValue,
22750             disabledDatesRE : this.ddMatch,
22751             disabledDatesText : this.disabledDatesText,
22752             disabledDays : this.disabledDays,
22753             disabledDaysText : this.disabledDaysText,
22754             format : this.format,
22755             minText : String.format(this.minText, this.formatDate(this.minValue)),
22756             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22757         });
22758         this.menu.on(Roo.apply({}, this.menuListeners, {
22759             scope:this
22760         }));
22761         this.menu.picker.setValue(this.getValue() || new Date());
22762         this.menu.show(this.el, "tl-bl?");
22763     },
22764
22765     beforeBlur : function(){
22766         var v = this.parseDate(this.getRawValue());
22767         if(v){
22768             this.setValue(v);
22769         }
22770     }
22771
22772     /** @cfg {Boolean} grow @hide */
22773     /** @cfg {Number} growMin @hide */
22774     /** @cfg {Number} growMax @hide */
22775     /**
22776      * @hide
22777      * @method autoSize
22778      */
22779 });/*
22780  * Based on:
22781  * Ext JS Library 1.1.1
22782  * Copyright(c) 2006-2007, Ext JS, LLC.
22783  *
22784  * Originally Released Under LGPL - original licence link has changed is not relivant.
22785  *
22786  * Fork - LGPL
22787  * <script type="text/javascript">
22788  */
22789  
22790
22791 /**
22792  * @class Roo.form.ComboBox
22793  * @extends Roo.form.TriggerField
22794  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22795  * @constructor
22796  * Create a new ComboBox.
22797  * @param {Object} config Configuration options
22798  */
22799 Roo.form.ComboBox = function(config){
22800     Roo.form.ComboBox.superclass.constructor.call(this, config);
22801     this.addEvents({
22802         /**
22803          * @event expand
22804          * Fires when the dropdown list is expanded
22805              * @param {Roo.form.ComboBox} combo This combo box
22806              */
22807         'expand' : true,
22808         /**
22809          * @event collapse
22810          * Fires when the dropdown list is collapsed
22811              * @param {Roo.form.ComboBox} combo This combo box
22812              */
22813         'collapse' : true,
22814         /**
22815          * @event beforeselect
22816          * Fires before a list item is selected. Return false to cancel the selection.
22817              * @param {Roo.form.ComboBox} combo This combo box
22818              * @param {Roo.data.Record} record The data record returned from the underlying store
22819              * @param {Number} index The index of the selected item in the dropdown list
22820              */
22821         'beforeselect' : true,
22822         /**
22823          * @event select
22824          * Fires when a list item is selected
22825              * @param {Roo.form.ComboBox} combo This combo box
22826              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22827              * @param {Number} index The index of the selected item in the dropdown list
22828              */
22829         'select' : true,
22830         /**
22831          * @event beforequery
22832          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22833          * The event object passed has these properties:
22834              * @param {Roo.form.ComboBox} combo This combo box
22835              * @param {String} query The query
22836              * @param {Boolean} forceAll true to force "all" query
22837              * @param {Boolean} cancel true to cancel the query
22838              * @param {Object} e The query event object
22839              */
22840         'beforequery': true,
22841          /**
22842          * @event add
22843          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22844              * @param {Roo.form.ComboBox} combo This combo box
22845              */
22846         'add' : true,
22847         /**
22848          * @event edit
22849          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22850              * @param {Roo.form.ComboBox} combo This combo box
22851              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22852              */
22853         'edit' : true
22854         
22855         
22856     });
22857     if(this.transform){
22858         this.allowDomMove = false;
22859         var s = Roo.getDom(this.transform);
22860         if(!this.hiddenName){
22861             this.hiddenName = s.name;
22862         }
22863         if(!this.store){
22864             this.mode = 'local';
22865             var d = [], opts = s.options;
22866             for(var i = 0, len = opts.length;i < len; i++){
22867                 var o = opts[i];
22868                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22869                 if(o.selected) {
22870                     this.value = value;
22871                 }
22872                 d.push([value, o.text]);
22873             }
22874             this.store = new Roo.data.SimpleStore({
22875                 'id': 0,
22876                 fields: ['value', 'text'],
22877                 data : d
22878             });
22879             this.valueField = 'value';
22880             this.displayField = 'text';
22881         }
22882         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22883         if(!this.lazyRender){
22884             this.target = true;
22885             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22886             s.parentNode.removeChild(s); // remove it
22887             this.render(this.el.parentNode);
22888         }else{
22889             s.parentNode.removeChild(s); // remove it
22890         }
22891
22892     }
22893     if (this.store) {
22894         this.store = Roo.factory(this.store, Roo.data);
22895     }
22896     
22897     this.selectedIndex = -1;
22898     if(this.mode == 'local'){
22899         if(config.queryDelay === undefined){
22900             this.queryDelay = 10;
22901         }
22902         if(config.minChars === undefined){
22903             this.minChars = 0;
22904         }
22905     }
22906 };
22907
22908 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22909     /**
22910      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22911      */
22912     /**
22913      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22914      * rendering into an Roo.Editor, defaults to false)
22915      */
22916     /**
22917      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22918      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22919      */
22920     /**
22921      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22922      */
22923     /**
22924      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22925      * the dropdown list (defaults to undefined, with no header element)
22926      */
22927
22928      /**
22929      * @cfg {String/Roo.Template} tpl The template to use to render the output
22930      */
22931      
22932     // private
22933     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22934     /**
22935      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22936      */
22937     listWidth: undefined,
22938     /**
22939      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22940      * mode = 'remote' or 'text' if mode = 'local')
22941      */
22942     displayField: undefined,
22943     /**
22944      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22945      * mode = 'remote' or 'value' if mode = 'local'). 
22946      * Note: use of a valueField requires the user make a selection
22947      * in order for a value to be mapped.
22948      */
22949     valueField: undefined,
22950     
22951     
22952     /**
22953      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22954      * field's data value (defaults to the underlying DOM element's name)
22955      */
22956     hiddenName: undefined,
22957     /**
22958      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22959      */
22960     listClass: '',
22961     /**
22962      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22963      */
22964     selectedClass: 'x-combo-selected',
22965     /**
22966      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22967      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22968      * which displays a downward arrow icon).
22969      */
22970     triggerClass : 'x-form-arrow-trigger',
22971     /**
22972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22973      */
22974     shadow:'sides',
22975     /**
22976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22977      * anchor positions (defaults to 'tl-bl')
22978      */
22979     listAlign: 'tl-bl?',
22980     /**
22981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22982      */
22983     maxHeight: 300,
22984     /**
22985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22986      * query specified by the allQuery config option (defaults to 'query')
22987      */
22988     triggerAction: 'query',
22989     /**
22990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22991      * (defaults to 4, does not apply if editable = false)
22992      */
22993     minChars : 4,
22994     /**
22995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22997      */
22998     typeAhead: false,
22999     /**
23000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23002      */
23003     queryDelay: 500,
23004     /**
23005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23007      */
23008     pageSize: 0,
23009     /**
23010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23011      * when editable = true (defaults to false)
23012      */
23013     selectOnFocus:false,
23014     /**
23015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23016      */
23017     queryParam: 'query',
23018     /**
23019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23020      * when mode = 'remote' (defaults to 'Loading...')
23021      */
23022     loadingText: 'Loading...',
23023     /**
23024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23025      */
23026     resizable: false,
23027     /**
23028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23029      */
23030     handleHeight : 8,
23031     /**
23032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23033      * traditional select (defaults to true)
23034      */
23035     editable: true,
23036     /**
23037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23038      */
23039     allQuery: '',
23040     /**
23041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23042      */
23043     mode: 'remote',
23044     /**
23045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23046      * listWidth has a higher value)
23047      */
23048     minListWidth : 70,
23049     /**
23050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23051      * allow the user to set arbitrary text into the field (defaults to false)
23052      */
23053     forceSelection:false,
23054     /**
23055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23056      * if typeAhead = true (defaults to 250)
23057      */
23058     typeAheadDelay : 250,
23059     /**
23060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23062      */
23063     valueNotFoundText : undefined,
23064     /**
23065      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23066      */
23067     blockFocus : false,
23068     
23069     /**
23070      * @cfg {Boolean} disableClear Disable showing of clear button.
23071      */
23072     disableClear : false,
23073     /**
23074      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23075      */
23076     alwaysQuery : false,
23077     
23078     //private
23079     addicon : false,
23080     editicon: false,
23081     
23082     // element that contains real text value.. (when hidden is used..)
23083      
23084     // private
23085     onRender : function(ct, position){
23086         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23087         if(this.hiddenName){
23088             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23089                     'before', true);
23090             this.hiddenField.value =
23091                 this.hiddenValue !== undefined ? this.hiddenValue :
23092                 this.value !== undefined ? this.value : '';
23093
23094             // prevent input submission
23095             this.el.dom.removeAttribute('name');
23096              
23097              
23098         }
23099         if(Roo.isGecko){
23100             this.el.dom.setAttribute('autocomplete', 'off');
23101         }
23102
23103         var cls = 'x-combo-list';
23104
23105         this.list = new Roo.Layer({
23106             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23107         });
23108
23109         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23110         this.list.setWidth(lw);
23111         this.list.swallowEvent('mousewheel');
23112         this.assetHeight = 0;
23113
23114         if(this.title){
23115             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23116             this.assetHeight += this.header.getHeight();
23117         }
23118
23119         this.innerList = this.list.createChild({cls:cls+'-inner'});
23120         this.innerList.on('mouseover', this.onViewOver, this);
23121         this.innerList.on('mousemove', this.onViewMove, this);
23122         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23123         
23124         if(this.allowBlank && !this.pageSize && !this.disableClear){
23125             this.footer = this.list.createChild({cls:cls+'-ft'});
23126             this.pageTb = new Roo.Toolbar(this.footer);
23127            
23128         }
23129         if(this.pageSize){
23130             this.footer = this.list.createChild({cls:cls+'-ft'});
23131             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23132                     {pageSize: this.pageSize});
23133             
23134         }
23135         
23136         if (this.pageTb && this.allowBlank && !this.disableClear) {
23137             var _this = this;
23138             this.pageTb.add(new Roo.Toolbar.Fill(), {
23139                 cls: 'x-btn-icon x-btn-clear',
23140                 text: '&#160;',
23141                 handler: function()
23142                 {
23143                     _this.collapse();
23144                     _this.clearValue();
23145                     _this.onSelect(false, -1);
23146                 }
23147             });
23148         }
23149         if (this.footer) {
23150             this.assetHeight += this.footer.getHeight();
23151         }
23152         
23153
23154         if(!this.tpl){
23155             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23156         }
23157
23158         this.view = new Roo.View(this.innerList, this.tpl, {
23159             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23160         });
23161
23162         this.view.on('click', this.onViewClick, this);
23163
23164         this.store.on('beforeload', this.onBeforeLoad, this);
23165         this.store.on('load', this.onLoad, this);
23166         this.store.on('loadexception', this.onLoadException, this);
23167
23168         if(this.resizable){
23169             this.resizer = new Roo.Resizable(this.list,  {
23170                pinned:true, handles:'se'
23171             });
23172             this.resizer.on('resize', function(r, w, h){
23173                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23174                 this.listWidth = w;
23175                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23176                 this.restrictHeight();
23177             }, this);
23178             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23179         }
23180         if(!this.editable){
23181             this.editable = true;
23182             this.setEditable(false);
23183         }  
23184         
23185         
23186         if (typeof(this.events.add.listeners) != 'undefined') {
23187             
23188             this.addicon = this.wrap.createChild(
23189                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23190        
23191             this.addicon.on('click', function(e) {
23192                 this.fireEvent('add', this);
23193             }, this);
23194         }
23195         if (typeof(this.events.edit.listeners) != 'undefined') {
23196             
23197             this.editicon = this.wrap.createChild(
23198                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23199             if (this.addicon) {
23200                 this.editicon.setStyle('margin-left', '40px');
23201             }
23202             this.editicon.on('click', function(e) {
23203                 
23204                 // we fire even  if inothing is selected..
23205                 this.fireEvent('edit', this, this.lastData );
23206                 
23207             }, this);
23208         }
23209         
23210         
23211         
23212     },
23213
23214     // private
23215     initEvents : function(){
23216         Roo.form.ComboBox.superclass.initEvents.call(this);
23217
23218         this.keyNav = new Roo.KeyNav(this.el, {
23219             "up" : function(e){
23220                 this.inKeyMode = true;
23221                 this.selectPrev();
23222             },
23223
23224             "down" : function(e){
23225                 if(!this.isExpanded()){
23226                     this.onTriggerClick();
23227                 }else{
23228                     this.inKeyMode = true;
23229                     this.selectNext();
23230                 }
23231             },
23232
23233             "enter" : function(e){
23234                 this.onViewClick();
23235                 //return true;
23236             },
23237
23238             "esc" : function(e){
23239                 this.collapse();
23240             },
23241
23242             "tab" : function(e){
23243                 this.onViewClick(false);
23244                 this.fireEvent("specialkey", this, e);
23245                 return true;
23246             },
23247
23248             scope : this,
23249
23250             doRelay : function(foo, bar, hname){
23251                 if(hname == 'down' || this.scope.isExpanded()){
23252                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23253                 }
23254                 return true;
23255             },
23256
23257             forceKeyDown: true
23258         });
23259         this.queryDelay = Math.max(this.queryDelay || 10,
23260                 this.mode == 'local' ? 10 : 250);
23261         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23262         if(this.typeAhead){
23263             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23264         }
23265         if(this.editable !== false){
23266             this.el.on("keyup", this.onKeyUp, this);
23267         }
23268         if(this.forceSelection){
23269             this.on('blur', this.doForce, this);
23270         }
23271     },
23272
23273     onDestroy : function(){
23274         if(this.view){
23275             this.view.setStore(null);
23276             this.view.el.removeAllListeners();
23277             this.view.el.remove();
23278             this.view.purgeListeners();
23279         }
23280         if(this.list){
23281             this.list.destroy();
23282         }
23283         if(this.store){
23284             this.store.un('beforeload', this.onBeforeLoad, this);
23285             this.store.un('load', this.onLoad, this);
23286             this.store.un('loadexception', this.onLoadException, this);
23287         }
23288         Roo.form.ComboBox.superclass.onDestroy.call(this);
23289     },
23290
23291     // private
23292     fireKey : function(e){
23293         if(e.isNavKeyPress() && !this.list.isVisible()){
23294             this.fireEvent("specialkey", this, e);
23295         }
23296     },
23297
23298     // private
23299     onResize: function(w, h){
23300         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23301         
23302         if(typeof w != 'number'){
23303             // we do not handle it!?!?
23304             return;
23305         }
23306         var tw = this.trigger.getWidth();
23307         tw += this.addicon ? this.addicon.getWidth() : 0;
23308         tw += this.editicon ? this.editicon.getWidth() : 0;
23309         var x = w - tw;
23310         this.el.setWidth( this.adjustWidth('input', x));
23311             
23312         this.trigger.setStyle('left', x+'px');
23313         
23314         if(this.list && this.listWidth === undefined){
23315             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23316             this.list.setWidth(lw);
23317             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23318         }
23319         
23320     
23321         
23322     },
23323
23324     /**
23325      * Allow or prevent the user from directly editing the field text.  If false is passed,
23326      * the user will only be able to select from the items defined in the dropdown list.  This method
23327      * is the runtime equivalent of setting the 'editable' config option at config time.
23328      * @param {Boolean} value True to allow the user to directly edit the field text
23329      */
23330     setEditable : function(value){
23331         if(value == this.editable){
23332             return;
23333         }
23334         this.editable = value;
23335         if(!value){
23336             this.el.dom.setAttribute('readOnly', true);
23337             this.el.on('mousedown', this.onTriggerClick,  this);
23338             this.el.addClass('x-combo-noedit');
23339         }else{
23340             this.el.dom.setAttribute('readOnly', false);
23341             this.el.un('mousedown', this.onTriggerClick,  this);
23342             this.el.removeClass('x-combo-noedit');
23343         }
23344     },
23345
23346     // private
23347     onBeforeLoad : function(){
23348         if(!this.hasFocus){
23349             return;
23350         }
23351         this.innerList.update(this.loadingText ?
23352                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23353         this.restrictHeight();
23354         this.selectedIndex = -1;
23355     },
23356
23357     // private
23358     onLoad : function(){
23359         if(!this.hasFocus){
23360             return;
23361         }
23362         if(this.store.getCount() > 0){
23363             this.expand();
23364             this.restrictHeight();
23365             if(this.lastQuery == this.allQuery){
23366                 if(this.editable){
23367                     this.el.dom.select();
23368                 }
23369                 if(!this.selectByValue(this.value, true)){
23370                     this.select(0, true);
23371                 }
23372             }else{
23373                 this.selectNext();
23374                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23375                     this.taTask.delay(this.typeAheadDelay);
23376                 }
23377             }
23378         }else{
23379             this.onEmptyResults();
23380         }
23381         //this.el.focus();
23382     },
23383     // private
23384     onLoadException : function()
23385     {
23386         this.collapse();
23387         Roo.log(this.store.reader.jsonData);
23388         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23389             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23390         }
23391         
23392         
23393     },
23394     // private
23395     onTypeAhead : function(){
23396         if(this.store.getCount() > 0){
23397             var r = this.store.getAt(0);
23398             var newValue = r.data[this.displayField];
23399             var len = newValue.length;
23400             var selStart = this.getRawValue().length;
23401             if(selStart != len){
23402                 this.setRawValue(newValue);
23403                 this.selectText(selStart, newValue.length);
23404             }
23405         }
23406     },
23407
23408     // private
23409     onSelect : function(record, index){
23410         if(this.fireEvent('beforeselect', this, record, index) !== false){
23411             this.setFromData(index > -1 ? record.data : false);
23412             this.collapse();
23413             this.fireEvent('select', this, record, index);
23414         }
23415     },
23416
23417     /**
23418      * Returns the currently selected field value or empty string if no value is set.
23419      * @return {String} value The selected value
23420      */
23421     getValue : function(){
23422         if(this.valueField){
23423             return typeof this.value != 'undefined' ? this.value : '';
23424         }else{
23425             return Roo.form.ComboBox.superclass.getValue.call(this);
23426         }
23427     },
23428
23429     /**
23430      * Clears any text/value currently set in the field
23431      */
23432     clearValue : function(){
23433         if(this.hiddenField){
23434             this.hiddenField.value = '';
23435         }
23436         this.value = '';
23437         this.setRawValue('');
23438         this.lastSelectionText = '';
23439         this.applyEmptyText();
23440     },
23441
23442     /**
23443      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23444      * will be displayed in the field.  If the value does not match the data value of an existing item,
23445      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23446      * Otherwise the field will be blank (although the value will still be set).
23447      * @param {String} value The value to match
23448      */
23449     setValue : function(v){
23450         var text = v;
23451         if(this.valueField){
23452             var r = this.findRecord(this.valueField, v);
23453             if(r){
23454                 text = r.data[this.displayField];
23455             }else if(this.valueNotFoundText !== undefined){
23456                 text = this.valueNotFoundText;
23457             }
23458         }
23459         this.lastSelectionText = text;
23460         if(this.hiddenField){
23461             this.hiddenField.value = v;
23462         }
23463         Roo.form.ComboBox.superclass.setValue.call(this, text);
23464         this.value = v;
23465     },
23466     /**
23467      * @property {Object} the last set data for the element
23468      */
23469     
23470     lastData : false,
23471     /**
23472      * Sets the value of the field based on a object which is related to the record format for the store.
23473      * @param {Object} value the value to set as. or false on reset?
23474      */
23475     setFromData : function(o){
23476         var dv = ''; // display value
23477         var vv = ''; // value value..
23478         this.lastData = o;
23479         if (this.displayField) {
23480             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23481         } else {
23482             // this is an error condition!!!
23483             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23484         }
23485         
23486         if(this.valueField){
23487             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23488         }
23489         if(this.hiddenField){
23490             this.hiddenField.value = vv;
23491             
23492             this.lastSelectionText = dv;
23493             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23494             this.value = vv;
23495             return;
23496         }
23497         // no hidden field.. - we store the value in 'value', but still display
23498         // display field!!!!
23499         this.lastSelectionText = dv;
23500         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23501         this.value = vv;
23502         
23503         
23504     },
23505     // private
23506     reset : function(){
23507         // overridden so that last data is reset..
23508         this.setValue(this.originalValue);
23509         this.clearInvalid();
23510         this.lastData = false;
23511     },
23512     // private
23513     findRecord : function(prop, value){
23514         var record;
23515         if(this.store.getCount() > 0){
23516             this.store.each(function(r){
23517                 if(r.data[prop] == value){
23518                     record = r;
23519                     return false;
23520                 }
23521                 return true;
23522             });
23523         }
23524         return record;
23525     },
23526     
23527     getName: function()
23528     {
23529         // returns hidden if it's set..
23530         if (!this.rendered) {return ''};
23531         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23532         
23533     },
23534     // private
23535     onViewMove : function(e, t){
23536         this.inKeyMode = false;
23537     },
23538
23539     // private
23540     onViewOver : function(e, t){
23541         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23542             return;
23543         }
23544         var item = this.view.findItemFromChild(t);
23545         if(item){
23546             var index = this.view.indexOf(item);
23547             this.select(index, false);
23548         }
23549     },
23550
23551     // private
23552     onViewClick : function(doFocus)
23553     {
23554         var index = this.view.getSelectedIndexes()[0];
23555         var r = this.store.getAt(index);
23556         if(r){
23557             this.onSelect(r, index);
23558         }
23559         if(doFocus !== false && !this.blockFocus){
23560             this.el.focus();
23561         }
23562     },
23563
23564     // private
23565     restrictHeight : function(){
23566         this.innerList.dom.style.height = '';
23567         var inner = this.innerList.dom;
23568         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23569         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23570         this.list.beginUpdate();
23571         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23572         this.list.alignTo(this.el, this.listAlign);
23573         this.list.endUpdate();
23574     },
23575
23576     // private
23577     onEmptyResults : function(){
23578         this.collapse();
23579     },
23580
23581     /**
23582      * Returns true if the dropdown list is expanded, else false.
23583      */
23584     isExpanded : function(){
23585         return this.list.isVisible();
23586     },
23587
23588     /**
23589      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23591      * @param {String} value The data value of the item to select
23592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23593      * selected item if it is not currently in view (defaults to true)
23594      * @return {Boolean} True if the value matched an item in the list, else false
23595      */
23596     selectByValue : function(v, scrollIntoView){
23597         if(v !== undefined && v !== null){
23598             var r = this.findRecord(this.valueField || this.displayField, v);
23599             if(r){
23600                 this.select(this.store.indexOf(r), scrollIntoView);
23601                 return true;
23602             }
23603         }
23604         return false;
23605     },
23606
23607     /**
23608      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23610      * @param {Number} index The zero-based index of the list item to select
23611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23612      * selected item if it is not currently in view (defaults to true)
23613      */
23614     select : function(index, scrollIntoView){
23615         this.selectedIndex = index;
23616         this.view.select(index);
23617         if(scrollIntoView !== false){
23618             var el = this.view.getNode(index);
23619             if(el){
23620                 this.innerList.scrollChildIntoView(el, false);
23621             }
23622         }
23623     },
23624
23625     // private
23626     selectNext : function(){
23627         var ct = this.store.getCount();
23628         if(ct > 0){
23629             if(this.selectedIndex == -1){
23630                 this.select(0);
23631             }else if(this.selectedIndex < ct-1){
23632                 this.select(this.selectedIndex+1);
23633             }
23634         }
23635     },
23636
23637     // private
23638     selectPrev : function(){
23639         var ct = this.store.getCount();
23640         if(ct > 0){
23641             if(this.selectedIndex == -1){
23642                 this.select(0);
23643             }else if(this.selectedIndex != 0){
23644                 this.select(this.selectedIndex-1);
23645             }
23646         }
23647     },
23648
23649     // private
23650     onKeyUp : function(e){
23651         if(this.editable !== false && !e.isSpecialKey()){
23652             this.lastKey = e.getKey();
23653             this.dqTask.delay(this.queryDelay);
23654         }
23655     },
23656
23657     // private
23658     validateBlur : function(){
23659         return !this.list || !this.list.isVisible();   
23660     },
23661
23662     // private
23663     initQuery : function(){
23664         this.doQuery(this.getRawValue());
23665     },
23666
23667     // private
23668     doForce : function(){
23669         if(this.el.dom.value.length > 0){
23670             this.el.dom.value =
23671                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23672             this.applyEmptyText();
23673         }
23674     },
23675
23676     /**
23677      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23678      * query allowing the query action to be canceled if needed.
23679      * @param {String} query The SQL query to execute
23680      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23681      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23682      * saved in the current store (defaults to false)
23683      */
23684     doQuery : function(q, forceAll){
23685         if(q === undefined || q === null){
23686             q = '';
23687         }
23688         var qe = {
23689             query: q,
23690             forceAll: forceAll,
23691             combo: this,
23692             cancel:false
23693         };
23694         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23695             return false;
23696         }
23697         q = qe.query;
23698         forceAll = qe.forceAll;
23699         if(forceAll === true || (q.length >= this.minChars)){
23700             if(this.lastQuery != q || this.alwaysQuery){
23701                 this.lastQuery = q;
23702                 if(this.mode == 'local'){
23703                     this.selectedIndex = -1;
23704                     if(forceAll){
23705                         this.store.clearFilter();
23706                     }else{
23707                         this.store.filter(this.displayField, q);
23708                     }
23709                     this.onLoad();
23710                 }else{
23711                     this.store.baseParams[this.queryParam] = q;
23712                     this.store.load({
23713                         params: this.getParams(q)
23714                     });
23715                     this.expand();
23716                 }
23717             }else{
23718                 this.selectedIndex = -1;
23719                 this.onLoad();   
23720             }
23721         }
23722     },
23723
23724     // private
23725     getParams : function(q){
23726         var p = {};
23727         //p[this.queryParam] = q;
23728         if(this.pageSize){
23729             p.start = 0;
23730             p.limit = this.pageSize;
23731         }
23732         return p;
23733     },
23734
23735     /**
23736      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23737      */
23738     collapse : function(){
23739         if(!this.isExpanded()){
23740             return;
23741         }
23742         this.list.hide();
23743         Roo.get(document).un('mousedown', this.collapseIf, this);
23744         Roo.get(document).un('mousewheel', this.collapseIf, this);
23745         if (!this.editable) {
23746             Roo.get(document).un('keydown', this.listKeyPress, this);
23747         }
23748         this.fireEvent('collapse', this);
23749     },
23750
23751     // private
23752     collapseIf : function(e){
23753         if(!e.within(this.wrap) && !e.within(this.list)){
23754             this.collapse();
23755         }
23756     },
23757
23758     /**
23759      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23760      */
23761     expand : function(){
23762         if(this.isExpanded() || !this.hasFocus){
23763             return;
23764         }
23765         this.list.alignTo(this.el, this.listAlign);
23766         this.list.show();
23767         Roo.get(document).on('mousedown', this.collapseIf, this);
23768         Roo.get(document).on('mousewheel', this.collapseIf, this);
23769         if (!this.editable) {
23770             Roo.get(document).on('keydown', this.listKeyPress, this);
23771         }
23772         
23773         this.fireEvent('expand', this);
23774     },
23775
23776     // private
23777     // Implements the default empty TriggerField.onTriggerClick function
23778     onTriggerClick : function(){
23779         if(this.disabled){
23780             return;
23781         }
23782         if(this.isExpanded()){
23783             this.collapse();
23784             if (!this.blockFocus) {
23785                 this.el.focus();
23786             }
23787             
23788         }else {
23789             this.hasFocus = true;
23790             if(this.triggerAction == 'all') {
23791                 this.doQuery(this.allQuery, true);
23792             } else {
23793                 this.doQuery(this.getRawValue());
23794             }
23795             if (!this.blockFocus) {
23796                 this.el.focus();
23797             }
23798         }
23799     },
23800     listKeyPress : function(e)
23801     {
23802         //Roo.log('listkeypress');
23803         // scroll to first matching element based on key pres..
23804         if (e.isSpecialKey()) {
23805             return false;
23806         }
23807         var k = String.fromCharCode(e.getKey()).toUpperCase();
23808         //Roo.log(k);
23809         var match  = false;
23810         var csel = this.view.getSelectedNodes();
23811         var cselitem = false;
23812         if (csel.length) {
23813             var ix = this.view.indexOf(csel[0]);
23814             cselitem  = this.store.getAt(ix);
23815             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23816                 cselitem = false;
23817             }
23818             
23819         }
23820         
23821         this.store.each(function(v) { 
23822             if (cselitem) {
23823                 // start at existing selection.
23824                 if (cselitem.id == v.id) {
23825                     cselitem = false;
23826                 }
23827                 return;
23828             }
23829                 
23830             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23831                 match = this.store.indexOf(v);
23832                 return false;
23833             }
23834         }, this);
23835         
23836         if (match === false) {
23837             return true; // no more action?
23838         }
23839         // scroll to?
23840         this.view.select(match);
23841         var sn = Roo.get(this.view.getSelectedNodes()[0])
23842         sn.scrollIntoView(sn.dom.parentNode, false);
23843     }
23844
23845     /** 
23846     * @cfg {Boolean} grow 
23847     * @hide 
23848     */
23849     /** 
23850     * @cfg {Number} growMin 
23851     * @hide 
23852     */
23853     /** 
23854     * @cfg {Number} growMax 
23855     * @hide 
23856     */
23857     /**
23858      * @hide
23859      * @method autoSize
23860      */
23861 });/*
23862  * Based on:
23863  * Ext JS Library 1.1.1
23864  * Copyright(c) 2006-2007, Ext JS, LLC.
23865  *
23866  * Originally Released Under LGPL - original licence link has changed is not relivant.
23867  *
23868  * Fork - LGPL
23869  * <script type="text/javascript">
23870  */
23871 /**
23872  * @class Roo.form.Checkbox
23873  * @extends Roo.form.Field
23874  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23875  * @constructor
23876  * Creates a new Checkbox
23877  * @param {Object} config Configuration options
23878  */
23879 Roo.form.Checkbox = function(config){
23880     Roo.form.Checkbox.superclass.constructor.call(this, config);
23881     this.addEvents({
23882         /**
23883          * @event check
23884          * Fires when the checkbox is checked or unchecked.
23885              * @param {Roo.form.Checkbox} this This checkbox
23886              * @param {Boolean} checked The new checked value
23887              */
23888         check : true
23889     });
23890 };
23891
23892 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23893     /**
23894      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23895      */
23896     focusClass : undefined,
23897     /**
23898      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23899      */
23900     fieldClass: "x-form-field",
23901     /**
23902      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23903      */
23904     checked: false,
23905     /**
23906      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23907      * {tag: "input", type: "checkbox", autocomplete: "off"})
23908      */
23909     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23910     /**
23911      * @cfg {String} boxLabel The text that appears beside the checkbox
23912      */
23913     boxLabel : "",
23914     /**
23915      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23916      */  
23917     inputValue : '1',
23918     /**
23919      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23920      */
23921      valueOff: '0', // value when not checked..
23922
23923     actionMode : 'viewEl', 
23924     //
23925     // private
23926     itemCls : 'x-menu-check-item x-form-item',
23927     groupClass : 'x-menu-group-item',
23928     inputType : 'hidden',
23929     
23930     
23931     inSetChecked: false, // check that we are not calling self...
23932     
23933     inputElement: false, // real input element?
23934     basedOn: false, // ????
23935     
23936     isFormField: true, // not sure where this is needed!!!!
23937
23938     onResize : function(){
23939         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23940         if(!this.boxLabel){
23941             this.el.alignTo(this.wrap, 'c-c');
23942         }
23943     },
23944
23945     initEvents : function(){
23946         Roo.form.Checkbox.superclass.initEvents.call(this);
23947         this.el.on("click", this.onClick,  this);
23948         this.el.on("change", this.onClick,  this);
23949     },
23950
23951
23952     getResizeEl : function(){
23953         return this.wrap;
23954     },
23955
23956     getPositionEl : function(){
23957         return this.wrap;
23958     },
23959
23960     // private
23961     onRender : function(ct, position){
23962         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23963         /*
23964         if(this.inputValue !== undefined){
23965             this.el.dom.value = this.inputValue;
23966         }
23967         */
23968         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23969         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23970         var viewEl = this.wrap.createChild({ 
23971             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23972         this.viewEl = viewEl;   
23973         this.wrap.on('click', this.onClick,  this); 
23974         
23975         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23976         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23977         
23978         
23979         
23980         if(this.boxLabel){
23981             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23982         //    viewEl.on('click', this.onClick,  this); 
23983         }
23984         //if(this.checked){
23985             this.setChecked(this.checked);
23986         //}else{
23987             //this.checked = this.el.dom;
23988         //}
23989
23990     },
23991
23992     // private
23993     initValue : Roo.emptyFn,
23994
23995     /**
23996      * Returns the checked state of the checkbox.
23997      * @return {Boolean} True if checked, else false
23998      */
23999     getValue : function(){
24000         if(this.el){
24001             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24002         }
24003         return this.valueOff;
24004         
24005     },
24006
24007         // private
24008     onClick : function(){ 
24009         this.setChecked(!this.checked);
24010
24011         //if(this.el.dom.checked != this.checked){
24012         //    this.setValue(this.el.dom.checked);
24013        // }
24014     },
24015
24016     /**
24017      * Sets the checked state of the checkbox.
24018      * On is always based on a string comparison between inputValue and the param.
24019      * @param {Boolean/String} value - the value to set 
24020      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24021      */
24022     setValue : function(v,suppressEvent){
24023         
24024         
24025         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24026         //if(this.el && this.el.dom){
24027         //    this.el.dom.checked = this.checked;
24028         //    this.el.dom.defaultChecked = this.checked;
24029         //}
24030         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24031         //this.fireEvent("check", this, this.checked);
24032     },
24033     // private..
24034     setChecked : function(state,suppressEvent)
24035     {
24036         if (this.inSetChecked) {
24037             this.checked = state;
24038             return;
24039         }
24040         
24041     
24042         if(this.wrap){
24043             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24044         }
24045         this.checked = state;
24046         if(suppressEvent !== true){
24047             this.fireEvent('check', this, state);
24048         }
24049         this.inSetChecked = true;
24050         this.el.dom.value = state ? this.inputValue : this.valueOff;
24051         this.inSetChecked = false;
24052         
24053     },
24054     // handle setting of hidden value by some other method!!?!?
24055     setFromHidden: function()
24056     {
24057         if(!this.el){
24058             return;
24059         }
24060         //console.log("SET FROM HIDDEN");
24061         //alert('setFrom hidden');
24062         this.setValue(this.el.dom.value);
24063     },
24064     
24065     onDestroy : function()
24066     {
24067         if(this.viewEl){
24068             Roo.get(this.viewEl).remove();
24069         }
24070          
24071         Roo.form.Checkbox.superclass.onDestroy.call(this);
24072     }
24073
24074 });/*
24075  * Based on:
24076  * Ext JS Library 1.1.1
24077  * Copyright(c) 2006-2007, Ext JS, LLC.
24078  *
24079  * Originally Released Under LGPL - original licence link has changed is not relivant.
24080  *
24081  * Fork - LGPL
24082  * <script type="text/javascript">
24083  */
24084  
24085 /**
24086  * @class Roo.form.Radio
24087  * @extends Roo.form.Checkbox
24088  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24089  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24090  * @constructor
24091  * Creates a new Radio
24092  * @param {Object} config Configuration options
24093  */
24094 Roo.form.Radio = function(){
24095     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24096 };
24097 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24098     inputType: 'radio',
24099
24100     /**
24101      * If this radio is part of a group, it will return the selected value
24102      * @return {String}
24103      */
24104     getGroupValue : function(){
24105         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24106     }
24107 });//<script type="text/javascript">
24108
24109 /*
24110  * Ext JS Library 1.1.1
24111  * Copyright(c) 2006-2007, Ext JS, LLC.
24112  * licensing@extjs.com
24113  * 
24114  * http://www.extjs.com/license
24115  */
24116  
24117  /*
24118   * 
24119   * Known bugs:
24120   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24121   * - IE ? - no idea how much works there.
24122   * 
24123   * 
24124   * 
24125   */
24126  
24127
24128 /**
24129  * @class Ext.form.HtmlEditor
24130  * @extends Ext.form.Field
24131  * Provides a lightweight HTML Editor component.
24132  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
24133  * 
24134  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24135  * supported by this editor.</b><br/><br/>
24136  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24137  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24138  */
24139 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24140       /**
24141      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24142      */
24143     toolbars : false,
24144     /**
24145      * @cfg {String} createLinkText The default text for the create link prompt
24146      */
24147     createLinkText : 'Please enter the URL for the link:',
24148     /**
24149      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24150      */
24151     defaultLinkValue : 'http:/'+'/',
24152    
24153      /**
24154      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24155      *                        Roo.resizable.
24156      */
24157     resizable : false,
24158      /**
24159      * @cfg {Number} height (in pixels)
24160      */   
24161     height: 300,
24162    /**
24163      * @cfg {Number} width (in pixels)
24164      */   
24165     width: 500,
24166     
24167     /**
24168      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24169      * 
24170      */
24171     stylesheets: false,
24172     
24173     // id of frame..
24174     frameId: false,
24175     
24176     // private properties
24177     validationEvent : false,
24178     deferHeight: true,
24179     initialized : false,
24180     activated : false,
24181     sourceEditMode : false,
24182     onFocus : Roo.emptyFn,
24183     iframePad:3,
24184     hideMode:'offsets',
24185     
24186     defaultAutoCreate : { // modified by initCompnoent..
24187         tag: "textarea",
24188         style:"width:500px;height:300px;",
24189         autocomplete: "off"
24190     },
24191
24192     // private
24193     initComponent : function(){
24194         this.addEvents({
24195             /**
24196              * @event initialize
24197              * Fires when the editor is fully initialized (including the iframe)
24198              * @param {HtmlEditor} this
24199              */
24200             initialize: true,
24201             /**
24202              * @event activate
24203              * Fires when the editor is first receives the focus. Any insertion must wait
24204              * until after this event.
24205              * @param {HtmlEditor} this
24206              */
24207             activate: true,
24208              /**
24209              * @event beforesync
24210              * Fires before the textarea is updated with content from the editor iframe. Return false
24211              * to cancel the sync.
24212              * @param {HtmlEditor} this
24213              * @param {String} html
24214              */
24215             beforesync: true,
24216              /**
24217              * @event beforepush
24218              * Fires before the iframe editor is updated with content from the textarea. Return false
24219              * to cancel the push.
24220              * @param {HtmlEditor} this
24221              * @param {String} html
24222              */
24223             beforepush: true,
24224              /**
24225              * @event sync
24226              * Fires when the textarea is updated with content from the editor iframe.
24227              * @param {HtmlEditor} this
24228              * @param {String} html
24229              */
24230             sync: true,
24231              /**
24232              * @event push
24233              * Fires when the iframe editor is updated with content from the textarea.
24234              * @param {HtmlEditor} this
24235              * @param {String} html
24236              */
24237             push: true,
24238              /**
24239              * @event editmodechange
24240              * Fires when the editor switches edit modes
24241              * @param {HtmlEditor} this
24242              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24243              */
24244             editmodechange: true,
24245             /**
24246              * @event editorevent
24247              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24248              * @param {HtmlEditor} this
24249              */
24250             editorevent: true
24251         });
24252         this.defaultAutoCreate =  {
24253             tag: "textarea",
24254             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24255             autocomplete: "off"
24256         };
24257     },
24258
24259     /**
24260      * Protected method that will not generally be called directly. It
24261      * is called when the editor creates its toolbar. Override this method if you need to
24262      * add custom toolbar buttons.
24263      * @param {HtmlEditor} editor
24264      */
24265     createToolbar : function(editor){
24266         if (!editor.toolbars || !editor.toolbars.length) {
24267             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24268         }
24269         
24270         for (var i =0 ; i < editor.toolbars.length;i++) {
24271             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24272             editor.toolbars[i].init(editor);
24273         }
24274          
24275         
24276     },
24277
24278     /**
24279      * Protected method that will not generally be called directly. It
24280      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24281      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24282      */
24283     getDocMarkup : function(){
24284         // body styles..
24285         var st = '';
24286         if (this.stylesheets === false) {
24287             
24288             Roo.get(document.head).select('style').each(function(node) {
24289                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24290             });
24291             
24292             Roo.get(document.head).select('link').each(function(node) { 
24293                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24294             });
24295             
24296         } else if (!this.stylesheets.length) {
24297                 // simple..
24298                 st = '<style type="text/css">' +
24299                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24300                    '</style>';
24301         } else {
24302             Roo.each(this.stylesheets, function(s) {
24303                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24304             });
24305             
24306         }
24307         
24308         return '<html><head>' + st  +
24309             //<style type="text/css">' +
24310             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24311             //'</style>' +
24312             ' </head><body></body></html>';
24313     },
24314
24315     // private
24316     onRender : function(ct, position)
24317     {
24318         var _t = this;
24319         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24320         this.el.dom.style.border = '0 none';
24321         this.el.dom.setAttribute('tabIndex', -1);
24322         this.el.addClass('x-hidden');
24323         if(Roo.isIE){ // fix IE 1px bogus margin
24324             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24325         }
24326         this.wrap = this.el.wrap({
24327             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24328         });
24329         
24330         if (this.resizable) {
24331             this.resizeEl = new Roo.Resizable(this.wrap, {
24332                 pinned : true,
24333                 wrap: true,
24334                 dynamic : true,
24335                 minHeight : this.height,
24336                 height: this.height,
24337                 handles : this.resizable,
24338                 width: this.width,
24339                 listeners : {
24340                     resize : function(r, w, h) {
24341                         _t.onResize(w,h); // -something
24342                     }
24343                 }
24344             });
24345             
24346         }
24347
24348         this.frameId = Roo.id();
24349         
24350         this.createToolbar(this);
24351         
24352       
24353         
24354         var iframe = this.wrap.createChild({
24355             tag: 'iframe',
24356             id: this.frameId,
24357             name: this.frameId,
24358             frameBorder : 'no',
24359             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24360         }, this.el
24361         );
24362         
24363        // console.log(iframe);
24364         //this.wrap.dom.appendChild(iframe);
24365
24366         this.iframe = iframe.dom;
24367
24368          this.assignDocWin();
24369         
24370         this.doc.designMode = 'on';
24371        
24372         this.doc.open();
24373         this.doc.write(this.getDocMarkup());
24374         this.doc.close();
24375
24376         
24377         var task = { // must defer to wait for browser to be ready
24378             run : function(){
24379                 //console.log("run task?" + this.doc.readyState);
24380                 this.assignDocWin();
24381                 if(this.doc.body || this.doc.readyState == 'complete'){
24382                     try {
24383                         this.doc.designMode="on";
24384                     } catch (e) {
24385                         return;
24386                     }
24387                     Roo.TaskMgr.stop(task);
24388                     this.initEditor.defer(10, this);
24389                 }
24390             },
24391             interval : 10,
24392             duration:10000,
24393             scope: this
24394         };
24395         Roo.TaskMgr.start(task);
24396
24397         if(!this.width){
24398             this.setSize(this.wrap.getSize());
24399         }
24400         if (this.resizeEl) {
24401             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24402             // should trigger onReize..
24403         }
24404     },
24405
24406     // private
24407     onResize : function(w, h)
24408     {
24409         //Roo.log('resize: ' +w + ',' + h );
24410         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24411         if(this.el && this.iframe){
24412             if(typeof w == 'number'){
24413                 var aw = w - this.wrap.getFrameWidth('lr');
24414                 this.el.setWidth(this.adjustWidth('textarea', aw));
24415                 this.iframe.style.width = aw + 'px';
24416             }
24417             if(typeof h == 'number'){
24418                 var tbh = 0;
24419                 for (var i =0; i < this.toolbars.length;i++) {
24420                     // fixme - ask toolbars for heights?
24421                     tbh += this.toolbars[i].tb.el.getHeight();
24422                     if (this.toolbars[i].footer) {
24423                         tbh += this.toolbars[i].footer.el.getHeight();
24424                     }
24425                 }
24426                 
24427                 
24428                 
24429                 
24430                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24431                 ah -= 5; // knock a few pixes off for look..
24432                 this.el.setHeight(this.adjustWidth('textarea', ah));
24433                 this.iframe.style.height = ah + 'px';
24434                 if(this.doc){
24435                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24436                 }
24437             }
24438         }
24439     },
24440
24441     /**
24442      * Toggles the editor between standard and source edit mode.
24443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24444      */
24445     toggleSourceEdit : function(sourceEditMode){
24446         
24447         this.sourceEditMode = sourceEditMode === true;
24448         
24449         if(this.sourceEditMode){
24450           
24451             this.syncValue();
24452             this.iframe.className = 'x-hidden';
24453             this.el.removeClass('x-hidden');
24454             this.el.dom.removeAttribute('tabIndex');
24455             this.el.focus();
24456         }else{
24457              
24458             this.pushValue();
24459             this.iframe.className = '';
24460             this.el.addClass('x-hidden');
24461             this.el.dom.setAttribute('tabIndex', -1);
24462             this.deferFocus();
24463         }
24464         this.setSize(this.wrap.getSize());
24465         this.fireEvent('editmodechange', this, this.sourceEditMode);
24466     },
24467
24468     // private used internally
24469     createLink : function(){
24470         var url = prompt(this.createLinkText, this.defaultLinkValue);
24471         if(url && url != 'http:/'+'/'){
24472             this.relayCmd('createlink', url);
24473         }
24474     },
24475
24476     // private (for BoxComponent)
24477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24478
24479     // private (for BoxComponent)
24480     getResizeEl : function(){
24481         return this.wrap;
24482     },
24483
24484     // private (for BoxComponent)
24485     getPositionEl : function(){
24486         return this.wrap;
24487     },
24488
24489     // private
24490     initEvents : function(){
24491         this.originalValue = this.getValue();
24492     },
24493
24494     /**
24495      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24496      * @method
24497      */
24498     markInvalid : Roo.emptyFn,
24499     /**
24500      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24501      * @method
24502      */
24503     clearInvalid : Roo.emptyFn,
24504
24505     setValue : function(v){
24506         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24507         this.pushValue();
24508     },
24509
24510     /**
24511      * Protected method that will not generally be called directly. If you need/want
24512      * custom HTML cleanup, this is the method you should override.
24513      * @param {String} html The HTML to be cleaned
24514      * return {String} The cleaned HTML
24515      */
24516     cleanHtml : function(html){
24517         html = String(html);
24518         if(html.length > 5){
24519             if(Roo.isSafari){ // strip safari nonsense
24520                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24521             }
24522         }
24523         if(html == '&nbsp;'){
24524             html = '';
24525         }
24526         return html;
24527     },
24528
24529     /**
24530      * Protected method that will not generally be called directly. Syncs the contents
24531      * of the editor iframe with the textarea.
24532      */
24533     syncValue : function(){
24534         if(this.initialized){
24535             var bd = (this.doc.body || this.doc.documentElement);
24536             //this.cleanUpPaste();
24537             var html = bd.innerHTML;
24538             if(Roo.isSafari){
24539                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24540                 var m = bs.match(/text-align:(.*?);/i);
24541                 if(m && m[1]){
24542                     html = '<div style="'+m[0]+'">' + html + '</div>';
24543                 }
24544             }
24545             html = this.cleanHtml(html);
24546             if(this.fireEvent('beforesync', this, html) !== false){
24547                 this.el.dom.value = html;
24548                 this.fireEvent('sync', this, html);
24549             }
24550         }
24551     },
24552
24553     /**
24554      * Protected method that will not generally be called directly. Pushes the value of the textarea
24555      * into the iframe editor.
24556      */
24557     pushValue : function(){
24558         if(this.initialized){
24559             var v = this.el.dom.value;
24560             if(v.length < 1){
24561                 v = '&#160;';
24562             }
24563             
24564             if(this.fireEvent('beforepush', this, v) !== false){
24565                 var d = (this.doc.body || this.doc.documentElement);
24566                 d.innerHTML = v;
24567                 this.cleanUpPaste();
24568                 this.el.dom.value = d.innerHTML;
24569                 this.fireEvent('push', this, v);
24570             }
24571         }
24572     },
24573
24574     // private
24575     deferFocus : function(){
24576         this.focus.defer(10, this);
24577     },
24578
24579     // doc'ed in Field
24580     focus : function(){
24581         if(this.win && !this.sourceEditMode){
24582             this.win.focus();
24583         }else{
24584             this.el.focus();
24585         }
24586     },
24587     
24588     assignDocWin: function()
24589     {
24590         var iframe = this.iframe;
24591         
24592          if(Roo.isIE){
24593             this.doc = iframe.contentWindow.document;
24594             this.win = iframe.contentWindow;
24595         } else {
24596             if (!Roo.get(this.frameId)) {
24597                 return;
24598             }
24599             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24600             this.win = Roo.get(this.frameId).dom.contentWindow;
24601         }
24602     },
24603     
24604     // private
24605     initEditor : function(){
24606         //console.log("INIT EDITOR");
24607         this.assignDocWin();
24608         
24609         
24610         
24611         this.doc.designMode="on";
24612         this.doc.open();
24613         this.doc.write(this.getDocMarkup());
24614         this.doc.close();
24615         
24616         var dbody = (this.doc.body || this.doc.documentElement);
24617         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24618         // this copies styles from the containing element into thsi one..
24619         // not sure why we need all of this..
24620         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24621         ss['background-attachment'] = 'fixed'; // w3c
24622         dbody.bgProperties = 'fixed'; // ie
24623         Roo.DomHelper.applyStyles(dbody, ss);
24624         Roo.EventManager.on(this.doc, {
24625             //'mousedown': this.onEditorEvent,
24626             'mouseup': this.onEditorEvent,
24627             'dblclick': this.onEditorEvent,
24628             'click': this.onEditorEvent,
24629             'keyup': this.onEditorEvent,
24630             buffer:100,
24631             scope: this
24632         });
24633         if(Roo.isGecko){
24634             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24635         }
24636         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24637             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24638         }
24639         this.initialized = true;
24640
24641         this.fireEvent('initialize', this);
24642         this.pushValue();
24643     },
24644
24645     // private
24646     onDestroy : function(){
24647         
24648         
24649         
24650         if(this.rendered){
24651             
24652             for (var i =0; i < this.toolbars.length;i++) {
24653                 // fixme - ask toolbars for heights?
24654                 this.toolbars[i].onDestroy();
24655             }
24656             
24657             this.wrap.dom.innerHTML = '';
24658             this.wrap.remove();
24659         }
24660     },
24661
24662     // private
24663     onFirstFocus : function(){
24664         
24665         this.assignDocWin();
24666         
24667         
24668         this.activated = true;
24669         for (var i =0; i < this.toolbars.length;i++) {
24670             this.toolbars[i].onFirstFocus();
24671         }
24672        
24673         if(Roo.isGecko){ // prevent silly gecko errors
24674             this.win.focus();
24675             var s = this.win.getSelection();
24676             if(!s.focusNode || s.focusNode.nodeType != 3){
24677                 var r = s.getRangeAt(0);
24678                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24679                 r.collapse(true);
24680                 this.deferFocus();
24681             }
24682             try{
24683                 this.execCmd('useCSS', true);
24684                 this.execCmd('styleWithCSS', false);
24685             }catch(e){}
24686         }
24687         this.fireEvent('activate', this);
24688     },
24689
24690     // private
24691     adjustFont: function(btn){
24692         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24693         //if(Roo.isSafari){ // safari
24694         //    adjust *= 2;
24695        // }
24696         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24697         if(Roo.isSafari){ // safari
24698             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24699             v =  (v < 10) ? 10 : v;
24700             v =  (v > 48) ? 48 : v;
24701             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24702             
24703         }
24704         
24705         
24706         v = Math.max(1, v+adjust);
24707         
24708         this.execCmd('FontSize', v  );
24709     },
24710
24711     onEditorEvent : function(e){
24712         this.fireEvent('editorevent', this, e);
24713       //  this.updateToolbar();
24714         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24715     },
24716
24717     insertTag : function(tg)
24718     {
24719         // could be a bit smarter... -> wrap the current selected tRoo..
24720         
24721         this.execCmd("formatblock",   tg);
24722         
24723     },
24724     
24725     insertText : function(txt)
24726     {
24727         
24728         
24729         range = this.createRange();
24730         range.deleteContents();
24731                //alert(Sender.getAttribute('label'));
24732                
24733         range.insertNode(this.doc.createTextNode(txt));
24734     } ,
24735     
24736     // private
24737     relayBtnCmd : function(btn){
24738         this.relayCmd(btn.cmd);
24739     },
24740
24741     /**
24742      * Executes a Midas editor command on the editor document and performs necessary focus and
24743      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24744      * @param {String} cmd The Midas command
24745      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24746      */
24747     relayCmd : function(cmd, value){
24748         this.win.focus();
24749         this.execCmd(cmd, value);
24750         this.fireEvent('editorevent', this);
24751         //this.updateToolbar();
24752         this.deferFocus();
24753     },
24754
24755     /**
24756      * Executes a Midas editor command directly on the editor document.
24757      * For visual commands, you should use {@link #relayCmd} instead.
24758      * <b>This should only be called after the editor is initialized.</b>
24759      * @param {String} cmd The Midas command
24760      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24761      */
24762     execCmd : function(cmd, value){
24763         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24764         this.syncValue();
24765     },
24766
24767    
24768     /**
24769      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24770      * to insert tRoo.
24771      * @param {String} text
24772      */
24773     insertAtCursor : function(text){
24774         if(!this.activated){
24775             return;
24776         }
24777         if(Roo.isIE){
24778             this.win.focus();
24779             var r = this.doc.selection.createRange();
24780             if(r){
24781                 r.collapse(true);
24782                 r.pasteHTML(text);
24783                 this.syncValue();
24784                 this.deferFocus();
24785             }
24786         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24787             this.win.focus();
24788             this.execCmd('InsertHTML', text);
24789             this.deferFocus();
24790         }
24791     },
24792  // private
24793     mozKeyPress : function(e){
24794         if(e.ctrlKey){
24795             var c = e.getCharCode(), cmd;
24796           
24797             if(c > 0){
24798                 c = String.fromCharCode(c).toLowerCase();
24799                 switch(c){
24800                     case 'b':
24801                         cmd = 'bold';
24802                     break;
24803                     case 'i':
24804                         cmd = 'italic';
24805                     break;
24806                     case 'u':
24807                         cmd = 'underline';
24808                         break;
24809                     case 'v':
24810                         this.cleanUpPaste.defer(100, this);
24811                         return;
24812                     break;
24813                 }
24814                 if(cmd){
24815                     this.win.focus();
24816                     this.execCmd(cmd);
24817                     this.deferFocus();
24818                     e.preventDefault();
24819                 }
24820                 
24821             }
24822         }
24823     },
24824
24825     // private
24826     fixKeys : function(){ // load time branching for fastest keydown performance
24827         if(Roo.isIE){
24828             return function(e){
24829                 var k = e.getKey(), r;
24830                 if(k == e.TAB){
24831                     e.stopEvent();
24832                     r = this.doc.selection.createRange();
24833                     if(r){
24834                         r.collapse(true);
24835                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24836                         this.deferFocus();
24837                     }
24838                     return;
24839                 }
24840                 
24841                 if(k == e.ENTER){
24842                     r = this.doc.selection.createRange();
24843                     if(r){
24844                         var target = r.parentElement();
24845                         if(!target || target.tagName.toLowerCase() != 'li'){
24846                             e.stopEvent();
24847                             r.pasteHTML('<br />');
24848                             r.collapse(false);
24849                             r.select();
24850                         }
24851                     }
24852                 }
24853                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24854                     this.cleanUpPaste.defer(100, this);
24855                     return;
24856                 }
24857                 
24858                 
24859             };
24860         }else if(Roo.isOpera){
24861             return function(e){
24862                 var k = e.getKey();
24863                 if(k == e.TAB){
24864                     e.stopEvent();
24865                     this.win.focus();
24866                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24867                     this.deferFocus();
24868                 }
24869                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24870                     this.cleanUpPaste.defer(100, this);
24871                     return;
24872                 }
24873                 
24874             };
24875         }else if(Roo.isSafari){
24876             return function(e){
24877                 var k = e.getKey();
24878                 
24879                 if(k == e.TAB){
24880                     e.stopEvent();
24881                     this.execCmd('InsertText','\t');
24882                     this.deferFocus();
24883                     return;
24884                 }
24885                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24886                     this.cleanUpPaste.defer(100, this);
24887                     return;
24888                 }
24889                 
24890              };
24891         }
24892     }(),
24893     
24894     getAllAncestors: function()
24895     {
24896         var p = this.getSelectedNode();
24897         var a = [];
24898         if (!p) {
24899             a.push(p); // push blank onto stack..
24900             p = this.getParentElement();
24901         }
24902         
24903         
24904         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24905             a.push(p);
24906             p = p.parentNode;
24907         }
24908         a.push(this.doc.body);
24909         return a;
24910     },
24911     lastSel : false,
24912     lastSelNode : false,
24913     
24914     
24915     getSelection : function() 
24916     {
24917         this.assignDocWin();
24918         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24919     },
24920     
24921     getSelectedNode: function() 
24922     {
24923         // this may only work on Gecko!!!
24924         
24925         // should we cache this!!!!
24926         
24927         
24928         
24929          
24930         var range = this.createRange(this.getSelection()).cloneRange();
24931         
24932         if (Roo.isIE) {
24933             var parent = range.parentElement();
24934             while (true) {
24935                 var testRange = range.duplicate();
24936                 testRange.moveToElementText(parent);
24937                 if (testRange.inRange(range)) {
24938                     break;
24939                 }
24940                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24941                     break;
24942                 }
24943                 parent = parent.parentElement;
24944             }
24945             return parent;
24946         }
24947         
24948         // is ancestor a text element.
24949         var ac =  range.commonAncestorContainer;
24950         if (ac.nodeType == 3) {
24951             ac = ac.parentNode;
24952         }
24953         
24954         var ar = ac.childNodes;
24955          
24956         var nodes = [];
24957         var other_nodes = [];
24958         var has_other_nodes = false;
24959         for (var i=0;i<ar.length;i++) {
24960             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24961                 continue;
24962             }
24963             // fullly contained node.
24964             
24965             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24966                 nodes.push(ar[i]);
24967                 continue;
24968             }
24969             
24970             // probably selected..
24971             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24972                 other_nodes.push(ar[i]);
24973                 continue;
24974             }
24975             // outer..
24976             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24977                 continue;
24978             }
24979             
24980             
24981             has_other_nodes = true;
24982         }
24983         if (!nodes.length && other_nodes.length) {
24984             nodes= other_nodes;
24985         }
24986         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24987             return false;
24988         }
24989         
24990         return nodes[0];
24991     },
24992     createRange: function(sel)
24993     {
24994         // this has strange effects when using with 
24995         // top toolbar - not sure if it's a great idea.
24996         //this.editor.contentWindow.focus();
24997         if (typeof sel != "undefined") {
24998             try {
24999                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25000             } catch(e) {
25001                 return this.doc.createRange();
25002             }
25003         } else {
25004             return this.doc.createRange();
25005         }
25006     },
25007     getParentElement: function()
25008     {
25009         
25010         this.assignDocWin();
25011         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25012         
25013         var range = this.createRange(sel);
25014          
25015         try {
25016             var p = range.commonAncestorContainer;
25017             while (p.nodeType == 3) { // text node
25018                 p = p.parentNode;
25019             }
25020             return p;
25021         } catch (e) {
25022             return null;
25023         }
25024     
25025     },
25026     /***
25027      *
25028      * Range intersection.. the hard stuff...
25029      *  '-1' = before
25030      *  '0' = hits..
25031      *  '1' = after.
25032      *         [ -- selected range --- ]
25033      *   [fail]                        [fail]
25034      *
25035      *    basically..
25036      *      if end is before start or  hits it. fail.
25037      *      if start is after end or hits it fail.
25038      *
25039      *   if either hits (but other is outside. - then it's not 
25040      *   
25041      *    
25042      **/
25043     
25044     
25045     // @see http://www.thismuchiknow.co.uk/?p=64.
25046     rangeIntersectsNode : function(range, node)
25047     {
25048         var nodeRange = node.ownerDocument.createRange();
25049         try {
25050             nodeRange.selectNode(node);
25051         } catch (e) {
25052             nodeRange.selectNodeContents(node);
25053         }
25054     
25055         var rangeStartRange = range.cloneRange();
25056         rangeStartRange.collapse(true);
25057     
25058         var rangeEndRange = range.cloneRange();
25059         rangeEndRange.collapse(false);
25060     
25061         var nodeStartRange = nodeRange.cloneRange();
25062         nodeStartRange.collapse(true);
25063     
25064         var nodeEndRange = nodeRange.cloneRange();
25065         nodeEndRange.collapse(false);
25066     
25067         return rangeStartRange.compareBoundaryPoints(
25068                  Range.START_TO_START, nodeEndRange) == -1 &&
25069                rangeEndRange.compareBoundaryPoints(
25070                  Range.START_TO_START, nodeStartRange) == 1;
25071         
25072          
25073     },
25074     rangeCompareNode : function(range, node)
25075     {
25076         var nodeRange = node.ownerDocument.createRange();
25077         try {
25078             nodeRange.selectNode(node);
25079         } catch (e) {
25080             nodeRange.selectNodeContents(node);
25081         }
25082         
25083         
25084         range.collapse(true);
25085     
25086         nodeRange.collapse(true);
25087      
25088         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25089         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25090          
25091         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25092         
25093         var nodeIsBefore   =  ss == 1;
25094         var nodeIsAfter    = ee == -1;
25095         
25096         if (nodeIsBefore && nodeIsAfter)
25097             return 0; // outer
25098         if (!nodeIsBefore && nodeIsAfter)
25099             return 1; //right trailed.
25100         
25101         if (nodeIsBefore && !nodeIsAfter)
25102             return 2;  // left trailed.
25103         // fully contined.
25104         return 3;
25105     },
25106
25107     // private? - in a new class?
25108     cleanUpPaste :  function()
25109     {
25110         // cleans up the whole document..
25111          Roo.log('cleanuppaste');
25112         this.cleanUpChildren(this.doc.body);
25113         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25114         if (clean != this.doc.body.innerHTML) {
25115             this.doc.body.innerHTML = clean;
25116         }
25117         
25118     },
25119     
25120     cleanWordChars : function(input) {
25121         var he = Roo.form.HtmlEditor;
25122     
25123         var output = input;
25124         Roo.each(he.swapCodes, function(sw) { 
25125         
25126             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25127             output = output.replace(swapper, sw[1]);
25128         });
25129         return output;
25130     },
25131     
25132     
25133     cleanUpChildren : function (n)
25134     {
25135         if (!n.childNodes.length) {
25136             return;
25137         }
25138         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25139            this.cleanUpChild(n.childNodes[i]);
25140         }
25141     },
25142     
25143     
25144         
25145     
25146     cleanUpChild : function (node)
25147     {
25148         //console.log(node);
25149         if (node.nodeName == "#text") {
25150             // clean up silly Windows -- stuff?
25151             return; 
25152         }
25153         if (node.nodeName == "#comment") {
25154             node.parentNode.removeChild(node);
25155             // clean up silly Windows -- stuff?
25156             return; 
25157         }
25158         
25159         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25160             // remove node.
25161             node.parentNode.removeChild(node);
25162             return;
25163             
25164         }
25165         
25166         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25167         
25168         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25169         
25170         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25171             remove_keep_children = true;
25172         }
25173         
25174         if (remove_keep_children) {
25175             this.cleanUpChildren(node);
25176             // inserts everything just before this node...
25177             while (node.childNodes.length) {
25178                 var cn = node.childNodes[0];
25179                 node.removeChild(cn);
25180                 node.parentNode.insertBefore(cn, node);
25181             }
25182             node.parentNode.removeChild(node);
25183             return;
25184         }
25185         
25186         if (!node.attributes || !node.attributes.length) {
25187             this.cleanUpChildren(node);
25188             return;
25189         }
25190         
25191         function cleanAttr(n,v)
25192         {
25193             
25194             if (v.match(/^\./) || v.match(/^\//)) {
25195                 return;
25196             }
25197             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25198                 return;
25199             }
25200             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25201             node.removeAttribute(n);
25202             
25203         }
25204         
25205         function cleanStyle(n,v)
25206         {
25207             if (v.match(/expression/)) { //XSS?? should we even bother..
25208                 node.removeAttribute(n);
25209                 return;
25210             }
25211             
25212             
25213             var parts = v.split(/;/);
25214             Roo.each(parts, function(p) {
25215                 p = p.replace(/\s+/g,'');
25216                 if (!p.length) {
25217                     return true;
25218                 }
25219                 var l = p.split(':').shift().replace(/\s+/g,'');
25220                 
25221                 // only allow 'c whitelisted system attributes'
25222                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25223                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25224                     node.removeAttribute(n);
25225                     return false;
25226                 }
25227                 return true;
25228             });
25229             
25230             
25231         }
25232         
25233         
25234         for (var i = node.attributes.length-1; i > -1 ; i--) {
25235             var a = node.attributes[i];
25236             //console.log(a);
25237             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25238                 node.removeAttribute(a.name);
25239                 return;
25240             }
25241             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25242                 cleanAttr(a.name,a.value); // fixme..
25243                 return;
25244             }
25245             if (a.name == 'style') {
25246                 cleanStyle(a.name,a.value);
25247             }
25248             /// clean up MS crap..
25249             // tecnically this should be a list of valid class'es..
25250             
25251             
25252             if (a.name == 'class') {
25253                 if (a.value.match(/^Mso/)) {
25254                     node.className = '';
25255                 }
25256                 
25257                 if (a.value.match(/body/)) {
25258                     node.className = '';
25259                 }
25260             }
25261             
25262             // style cleanup!?
25263             // class cleanup?
25264             
25265         }
25266         
25267         
25268         this.cleanUpChildren(node);
25269         
25270         
25271     }
25272     
25273     
25274     // hide stuff that is not compatible
25275     /**
25276      * @event blur
25277      * @hide
25278      */
25279     /**
25280      * @event change
25281      * @hide
25282      */
25283     /**
25284      * @event focus
25285      * @hide
25286      */
25287     /**
25288      * @event specialkey
25289      * @hide
25290      */
25291     /**
25292      * @cfg {String} fieldClass @hide
25293      */
25294     /**
25295      * @cfg {String} focusClass @hide
25296      */
25297     /**
25298      * @cfg {String} autoCreate @hide
25299      */
25300     /**
25301      * @cfg {String} inputType @hide
25302      */
25303     /**
25304      * @cfg {String} invalidClass @hide
25305      */
25306     /**
25307      * @cfg {String} invalidText @hide
25308      */
25309     /**
25310      * @cfg {String} msgFx @hide
25311      */
25312     /**
25313      * @cfg {String} validateOnBlur @hide
25314      */
25315 });
25316
25317 Roo.form.HtmlEditor.white = [
25318         'area', 'br', 'img', 'input', 'hr', 'wbr',
25319         
25320        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25321        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25322        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25323        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25324        'table',   'ul',         'xmp', 
25325        
25326        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25327       'thead',   'tr', 
25328      
25329       'dir', 'menu', 'ol', 'ul', 'dl',
25330        
25331       'embed',  'object'
25332 ];
25333
25334
25335 Roo.form.HtmlEditor.black = [
25336     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25337         'applet', // 
25338         'base',   'basefont', 'bgsound', 'blink',  'body', 
25339         'frame',  'frameset', 'head',    'html',   'ilayer', 
25340         'iframe', 'layer',  'link',     'meta',    'object',   
25341         'script', 'style' ,'title',  'xml' // clean later..
25342 ];
25343 Roo.form.HtmlEditor.clean = [
25344     'script', 'style', 'title', 'xml'
25345 ];
25346 Roo.form.HtmlEditor.remove = [
25347     'font'
25348 ];
25349 // attributes..
25350
25351 Roo.form.HtmlEditor.ablack = [
25352     'on'
25353 ];
25354     
25355 Roo.form.HtmlEditor.aclean = [ 
25356     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25357 ];
25358
25359 // protocols..
25360 Roo.form.HtmlEditor.pwhite= [
25361         'http',  'https',  'mailto'
25362 ];
25363
25364 // white listed style attributes.
25365 Roo.form.HtmlEditor.cwhite= [
25366         'text-align',
25367         'font-size'
25368 ];
25369
25370
25371 Roo.form.HtmlEditor.swapCodes   =[ 
25372     [    8211, "--" ], 
25373     [    8212, "--" ], 
25374     [    8216,  "'" ],  
25375     [    8217, "'" ],  
25376     [    8220, '"' ],  
25377     [    8221, '"' ],  
25378     [    8226, "*" ],  
25379     [    8230, "..." ]
25380 ]; 
25381
25382     // <script type="text/javascript">
25383 /*
25384  * Based on
25385  * Ext JS Library 1.1.1
25386  * Copyright(c) 2006-2007, Ext JS, LLC.
25387  *  
25388  
25389  */
25390
25391 /**
25392  * @class Roo.form.HtmlEditorToolbar1
25393  * Basic Toolbar
25394  * 
25395  * Usage:
25396  *
25397  new Roo.form.HtmlEditor({
25398     ....
25399     toolbars : [
25400         new Roo.form.HtmlEditorToolbar1({
25401             disable : { fonts: 1 , format: 1, ..., ... , ...],
25402             btns : [ .... ]
25403         })
25404     }
25405      
25406  * 
25407  * @cfg {Object} disable List of elements to disable..
25408  * @cfg {Array} btns List of additional buttons.
25409  * 
25410  * 
25411  * NEEDS Extra CSS? 
25412  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25413  */
25414  
25415 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25416 {
25417     
25418     Roo.apply(this, config);
25419     
25420     // default disabled, based on 'good practice'..
25421     this.disable = this.disable || {};
25422     Roo.applyIf(this.disable, {
25423         fontSize : true,
25424         colors : true,
25425         specialElements : true
25426     });
25427     
25428     
25429     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25430     // dont call parent... till later.
25431 }
25432
25433 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25434     
25435     tb: false,
25436     
25437     rendered: false,
25438     
25439     editor : false,
25440     /**
25441      * @cfg {Object} disable  List of toolbar elements to disable
25442          
25443      */
25444     disable : false,
25445       /**
25446      * @cfg {Array} fontFamilies An array of available font families
25447      */
25448     fontFamilies : [
25449         'Arial',
25450         'Courier New',
25451         'Tahoma',
25452         'Times New Roman',
25453         'Verdana'
25454     ],
25455     
25456     specialChars : [
25457            "&#169;",
25458           "&#174;",     
25459           "&#8482;",    
25460           "&#163;" ,    
25461          // "&#8212;",    
25462           "&#8230;",    
25463           "&#247;" ,    
25464         //  "&#225;" ,     ?? a acute?
25465            "&#8364;"    , //Euro
25466        //   "&#8220;"    ,
25467         //  "&#8221;"    ,
25468         //  "&#8226;"    ,
25469           "&#176;"  //   , // degrees
25470
25471          // "&#233;"     , // e ecute
25472          // "&#250;"     , // u ecute?
25473     ],
25474     
25475     specialElements : [
25476         {
25477             text: "Insert Table",
25478             xtype: 'MenuItem',
25479             xns : Roo.Menu,
25480             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
25481                 
25482         },
25483         {    
25484             text: "Insert Image",
25485             xtype: 'MenuItem',
25486             xns : Roo.Menu,
25487             ihtml : '<img src="about:blank"/>'
25488             
25489         }
25490         
25491          
25492     ],
25493     
25494     
25495     inputElements : [ 
25496             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25497             "input:submit", "input:button", "select", "textarea", "label" ],
25498     formats : [
25499         ["p"] ,  
25500         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25501         ["pre"],[ "code"], 
25502         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25503     ],
25504      /**
25505      * @cfg {String} defaultFont default font to use.
25506      */
25507     defaultFont: 'tahoma',
25508    
25509     fontSelect : false,
25510     
25511     
25512     formatCombo : false,
25513     
25514     init : function(editor)
25515     {
25516         this.editor = editor;
25517         
25518         
25519         var fid = editor.frameId;
25520         var etb = this;
25521         function btn(id, toggle, handler){
25522             var xid = fid + '-'+ id ;
25523             return {
25524                 id : xid,
25525                 cmd : id,
25526                 cls : 'x-btn-icon x-edit-'+id,
25527                 enableToggle:toggle !== false,
25528                 scope: editor, // was editor...
25529                 handler:handler||editor.relayBtnCmd,
25530                 clickEvent:'mousedown',
25531                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25532                 tabIndex:-1
25533             };
25534         }
25535         
25536         
25537         
25538         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25539         this.tb = tb;
25540          // stop form submits
25541         tb.el.on('click', function(e){
25542             e.preventDefault(); // what does this do?
25543         });
25544
25545         if(!this.disable.font && !Roo.isSafari){
25546             /* why no safari for fonts
25547             editor.fontSelect = tb.el.createChild({
25548                 tag:'select',
25549                 tabIndex: -1,
25550                 cls:'x-font-select',
25551                 html: editor.createFontOptions()
25552             });
25553             editor.fontSelect.on('change', function(){
25554                 var font = editor.fontSelect.dom.value;
25555                 editor.relayCmd('fontname', font);
25556                 editor.deferFocus();
25557             }, editor);
25558             tb.add(
25559                 editor.fontSelect.dom,
25560                 '-'
25561             );
25562             */
25563         };
25564         if(!this.disable.formats){
25565             this.formatCombo = new Roo.form.ComboBox({
25566                 store: new Roo.data.SimpleStore({
25567                     id : 'tag',
25568                     fields: ['tag'],
25569                     data : this.formats // from states.js
25570                 }),
25571                 blockFocus : true,
25572                 //autoCreate : {tag: "div",  size: "20"},
25573                 displayField:'tag',
25574                 typeAhead: false,
25575                 mode: 'local',
25576                 editable : false,
25577                 triggerAction: 'all',
25578                 emptyText:'Add tag',
25579                 selectOnFocus:true,
25580                 width:135,
25581                 listeners : {
25582                     'select': function(c, r, i) {
25583                         editor.insertTag(r.get('tag'));
25584                         editor.focus();
25585                     }
25586                 }
25587
25588             });
25589             tb.addField(this.formatCombo);
25590             
25591         }
25592         
25593         if(!this.disable.format){
25594             tb.add(
25595                 btn('bold'),
25596                 btn('italic'),
25597                 btn('underline')
25598             );
25599         };
25600         if(!this.disable.fontSize){
25601             tb.add(
25602                 '-',
25603                 
25604                 
25605                 btn('increasefontsize', false, editor.adjustFont),
25606                 btn('decreasefontsize', false, editor.adjustFont)
25607             );
25608         };
25609         
25610         
25611         if(!this.disable.colors){
25612             tb.add(
25613                 '-', {
25614                     id:editor.frameId +'-forecolor',
25615                     cls:'x-btn-icon x-edit-forecolor',
25616                     clickEvent:'mousedown',
25617                     tooltip: this.buttonTips['forecolor'] || undefined,
25618                     tabIndex:-1,
25619                     menu : new Roo.menu.ColorMenu({
25620                         allowReselect: true,
25621                         focus: Roo.emptyFn,
25622                         value:'000000',
25623                         plain:true,
25624                         selectHandler: function(cp, color){
25625                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25626                             editor.deferFocus();
25627                         },
25628                         scope: editor,
25629                         clickEvent:'mousedown'
25630                     })
25631                 }, {
25632                     id:editor.frameId +'backcolor',
25633                     cls:'x-btn-icon x-edit-backcolor',
25634                     clickEvent:'mousedown',
25635                     tooltip: this.buttonTips['backcolor'] || undefined,
25636                     tabIndex:-1,
25637                     menu : new Roo.menu.ColorMenu({
25638                         focus: Roo.emptyFn,
25639                         value:'FFFFFF',
25640                         plain:true,
25641                         allowReselect: true,
25642                         selectHandler: function(cp, color){
25643                             if(Roo.isGecko){
25644                                 editor.execCmd('useCSS', false);
25645                                 editor.execCmd('hilitecolor', color);
25646                                 editor.execCmd('useCSS', true);
25647                                 editor.deferFocus();
25648                             }else{
25649                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25650                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25651                                 editor.deferFocus();
25652                             }
25653                         },
25654                         scope:editor,
25655                         clickEvent:'mousedown'
25656                     })
25657                 }
25658             );
25659         };
25660         // now add all the items...
25661         
25662
25663         if(!this.disable.alignments){
25664             tb.add(
25665                 '-',
25666                 btn('justifyleft'),
25667                 btn('justifycenter'),
25668                 btn('justifyright')
25669             );
25670         };
25671
25672         //if(!Roo.isSafari){
25673             if(!this.disable.links){
25674                 tb.add(
25675                     '-',
25676                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25677                 );
25678             };
25679
25680             if(!this.disable.lists){
25681                 tb.add(
25682                     '-',
25683                     btn('insertorderedlist'),
25684                     btn('insertunorderedlist')
25685                 );
25686             }
25687             if(!this.disable.sourceEdit){
25688                 tb.add(
25689                     '-',
25690                     btn('sourceedit', true, function(btn){
25691                         this.toggleSourceEdit(btn.pressed);
25692                     })
25693                 );
25694             }
25695         //}
25696         
25697         var smenu = { };
25698         // special menu.. - needs to be tidied up..
25699         if (!this.disable.special) {
25700             smenu = {
25701                 text: "&#169;",
25702                 cls: 'x-edit-none',
25703                 
25704                 menu : {
25705                     items : []
25706                 }
25707             };
25708             for (var i =0; i < this.specialChars.length; i++) {
25709                 smenu.menu.items.push({
25710                     
25711                     html: this.specialChars[i],
25712                     handler: function(a,b) {
25713                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25714                         
25715                     },
25716                     tabIndex:-1
25717                 });
25718             }
25719             
25720             
25721             tb.add(smenu);
25722             
25723             
25724         }
25725          
25726         if (!this.disable.specialElements) {
25727             var semenu = {
25728                 text: "Other;",
25729                 cls: 'x-edit-none',
25730                 menu : {
25731                     items : []
25732                 }
25733             };
25734             for (var i =0; i < this.specialElements.length; i++) {
25735                 semenu.menu.items.push(
25736                     Roo.apply({ 
25737                         handler: function(a,b) {
25738                             editor.insertAtCursor(this.ihtml);
25739                         }
25740                     }, this.specialElements[i])
25741                 );
25742                     
25743             }
25744             
25745             tb.add(semenu);
25746             
25747             
25748         }
25749          
25750         
25751         if (this.btns) {
25752             for(var i =0; i< this.btns.length;i++) {
25753                 var b = this.btns[i];
25754                 b.cls =  'x-edit-none';
25755                 b.scope = editor;
25756                 tb.add(b);
25757             }
25758         
25759         }
25760         
25761         
25762         
25763         // disable everything...
25764         
25765         this.tb.items.each(function(item){
25766            if(item.id != editor.frameId+ '-sourceedit'){
25767                 item.disable();
25768             }
25769         });
25770         this.rendered = true;
25771         
25772         // the all the btns;
25773         editor.on('editorevent', this.updateToolbar, this);
25774         // other toolbars need to implement this..
25775         //editor.on('editmodechange', this.updateToolbar, this);
25776     },
25777     
25778     
25779     
25780     /**
25781      * Protected method that will not generally be called directly. It triggers
25782      * a toolbar update by reading the markup state of the current selection in the editor.
25783      */
25784     updateToolbar: function(){
25785
25786         if(!this.editor.activated){
25787             this.editor.onFirstFocus();
25788             return;
25789         }
25790
25791         var btns = this.tb.items.map, 
25792             doc = this.editor.doc,
25793             frameId = this.editor.frameId;
25794
25795         if(!this.disable.font && !Roo.isSafari){
25796             /*
25797             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25798             if(name != this.fontSelect.dom.value){
25799                 this.fontSelect.dom.value = name;
25800             }
25801             */
25802         }
25803         if(!this.disable.format){
25804             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25805             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25806             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25807         }
25808         if(!this.disable.alignments){
25809             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25810             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25811             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25812         }
25813         if(!Roo.isSafari && !this.disable.lists){
25814             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25815             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25816         }
25817         
25818         var ans = this.editor.getAllAncestors();
25819         if (this.formatCombo) {
25820             
25821             
25822             var store = this.formatCombo.store;
25823             this.formatCombo.setValue("");
25824             for (var i =0; i < ans.length;i++) {
25825                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25826                     // select it..
25827                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25828                     break;
25829                 }
25830             }
25831         }
25832         
25833         
25834         
25835         // hides menus... - so this cant be on a menu...
25836         Roo.menu.MenuMgr.hideAll();
25837
25838         //this.editorsyncValue();
25839     },
25840    
25841     
25842     createFontOptions : function(){
25843         var buf = [], fs = this.fontFamilies, ff, lc;
25844         for(var i = 0, len = fs.length; i< len; i++){
25845             ff = fs[i];
25846             lc = ff.toLowerCase();
25847             buf.push(
25848                 '<option value="',lc,'" style="font-family:',ff,';"',
25849                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25850                     ff,
25851                 '</option>'
25852             );
25853         }
25854         return buf.join('');
25855     },
25856     
25857     toggleSourceEdit : function(sourceEditMode){
25858         if(sourceEditMode === undefined){
25859             sourceEditMode = !this.sourceEditMode;
25860         }
25861         this.sourceEditMode = sourceEditMode === true;
25862         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25863         // just toggle the button?
25864         if(btn.pressed !== this.editor.sourceEditMode){
25865             btn.toggle(this.editor.sourceEditMode);
25866             return;
25867         }
25868         
25869         if(this.sourceEditMode){
25870             this.tb.items.each(function(item){
25871                 if(item.cmd != 'sourceedit'){
25872                     item.disable();
25873                 }
25874             });
25875           
25876         }else{
25877             if(this.initialized){
25878                 this.tb.items.each(function(item){
25879                     item.enable();
25880                 });
25881             }
25882             
25883         }
25884         // tell the editor that it's been pressed..
25885         this.editor.toggleSourceEdit(sourceEditMode);
25886        
25887     },
25888      /**
25889      * Object collection of toolbar tooltips for the buttons in the editor. The key
25890      * is the command id associated with that button and the value is a valid QuickTips object.
25891      * For example:
25892 <pre><code>
25893 {
25894     bold : {
25895         title: 'Bold (Ctrl+B)',
25896         text: 'Make the selected text bold.',
25897         cls: 'x-html-editor-tip'
25898     },
25899     italic : {
25900         title: 'Italic (Ctrl+I)',
25901         text: 'Make the selected text italic.',
25902         cls: 'x-html-editor-tip'
25903     },
25904     ...
25905 </code></pre>
25906     * @type Object
25907      */
25908     buttonTips : {
25909         bold : {
25910             title: 'Bold (Ctrl+B)',
25911             text: 'Make the selected text bold.',
25912             cls: 'x-html-editor-tip'
25913         },
25914         italic : {
25915             title: 'Italic (Ctrl+I)',
25916             text: 'Make the selected text italic.',
25917             cls: 'x-html-editor-tip'
25918         },
25919         underline : {
25920             title: 'Underline (Ctrl+U)',
25921             text: 'Underline the selected text.',
25922             cls: 'x-html-editor-tip'
25923         },
25924         increasefontsize : {
25925             title: 'Grow Text',
25926             text: 'Increase the font size.',
25927             cls: 'x-html-editor-tip'
25928         },
25929         decreasefontsize : {
25930             title: 'Shrink Text',
25931             text: 'Decrease the font size.',
25932             cls: 'x-html-editor-tip'
25933         },
25934         backcolor : {
25935             title: 'Text Highlight Color',
25936             text: 'Change the background color of the selected text.',
25937             cls: 'x-html-editor-tip'
25938         },
25939         forecolor : {
25940             title: 'Font Color',
25941             text: 'Change the color of the selected text.',
25942             cls: 'x-html-editor-tip'
25943         },
25944         justifyleft : {
25945             title: 'Align Text Left',
25946             text: 'Align text to the left.',
25947             cls: 'x-html-editor-tip'
25948         },
25949         justifycenter : {
25950             title: 'Center Text',
25951             text: 'Center text in the editor.',
25952             cls: 'x-html-editor-tip'
25953         },
25954         justifyright : {
25955             title: 'Align Text Right',
25956             text: 'Align text to the right.',
25957             cls: 'x-html-editor-tip'
25958         },
25959         insertunorderedlist : {
25960             title: 'Bullet List',
25961             text: 'Start a bulleted list.',
25962             cls: 'x-html-editor-tip'
25963         },
25964         insertorderedlist : {
25965             title: 'Numbered List',
25966             text: 'Start a numbered list.',
25967             cls: 'x-html-editor-tip'
25968         },
25969         createlink : {
25970             title: 'Hyperlink',
25971             text: 'Make the selected text a hyperlink.',
25972             cls: 'x-html-editor-tip'
25973         },
25974         sourceedit : {
25975             title: 'Source Edit',
25976             text: 'Switch to source editing mode.',
25977             cls: 'x-html-editor-tip'
25978         }
25979     },
25980     // private
25981     onDestroy : function(){
25982         if(this.rendered){
25983             
25984             this.tb.items.each(function(item){
25985                 if(item.menu){
25986                     item.menu.removeAll();
25987                     if(item.menu.el){
25988                         item.menu.el.destroy();
25989                     }
25990                 }
25991                 item.destroy();
25992             });
25993              
25994         }
25995     },
25996     onFirstFocus: function() {
25997         this.tb.items.each(function(item){
25998            item.enable();
25999         });
26000     }
26001 });
26002
26003
26004
26005
26006 // <script type="text/javascript">
26007 /*
26008  * Based on
26009  * Ext JS Library 1.1.1
26010  * Copyright(c) 2006-2007, Ext JS, LLC.
26011  *  
26012  
26013  */
26014
26015  
26016 /**
26017  * @class Roo.form.HtmlEditor.ToolbarContext
26018  * Context Toolbar
26019  * 
26020  * Usage:
26021  *
26022  new Roo.form.HtmlEditor({
26023     ....
26024     toolbars : [
26025         { xtype: 'ToolbarStandard', styles : {} }
26026         { xtype: 'ToolbarContext', disable : {} }
26027     ]
26028 })
26029
26030      
26031  * 
26032  * @config : {Object} disable List of elements to disable.. (not done yet.)
26033  * @config : {Object} styles  Map of styles available.
26034  * 
26035  */
26036
26037 Roo.form.HtmlEditor.ToolbarContext = function(config)
26038 {
26039     
26040     Roo.apply(this, config);
26041     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26042     // dont call parent... till later.
26043     this.styles = this.styles || {};
26044 }
26045 Roo.form.HtmlEditor.ToolbarContext.types = {
26046     'IMG' : {
26047         width : {
26048             title: "Width",
26049             width: 40
26050         },
26051         height:  {
26052             title: "Height",
26053             width: 40
26054         },
26055         align: {
26056             title: "Align",
26057             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26058             width : 80
26059             
26060         },
26061         border: {
26062             title: "Border",
26063             width: 40
26064         },
26065         alt: {
26066             title: "Alt",
26067             width: 120
26068         },
26069         src : {
26070             title: "Src",
26071             width: 220
26072         }
26073         
26074     },
26075     'A' : {
26076         name : {
26077             title: "Name",
26078             width: 50
26079         },
26080         href:  {
26081             title: "Href",
26082             width: 220
26083         } // border?
26084         
26085     },
26086     'TABLE' : {
26087         rows : {
26088             title: "Rows",
26089             width: 20
26090         },
26091         cols : {
26092             title: "Cols",
26093             width: 20
26094         },
26095         width : {
26096             title: "Width",
26097             width: 40
26098         },
26099         height : {
26100             title: "Height",
26101             width: 40
26102         },
26103         border : {
26104             title: "Border",
26105             width: 20
26106         }
26107     },
26108     'TD' : {
26109         width : {
26110             title: "Width",
26111             width: 40
26112         },
26113         height : {
26114             title: "Height",
26115             width: 40
26116         },   
26117         align: {
26118             title: "Align",
26119             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26120             width: 80
26121         },
26122         valign: {
26123             title: "Valign",
26124             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26125             width: 80
26126         },
26127         colspan: {
26128             title: "Colspan",
26129             width: 20
26130             
26131         }
26132     },
26133     'INPUT' : {
26134         name : {
26135             title: "name",
26136             width: 120
26137         },
26138         value : {
26139             title: "Value",
26140             width: 120
26141         },
26142         width : {
26143             title: "Width",
26144             width: 40
26145         }
26146     },
26147     'LABEL' : {
26148         'for' : {
26149             title: "For",
26150             width: 120
26151         }
26152     },
26153     'TEXTAREA' : {
26154           name : {
26155             title: "name",
26156             width: 120
26157         },
26158         rows : {
26159             title: "Rows",
26160             width: 20
26161         },
26162         cols : {
26163             title: "Cols",
26164             width: 20
26165         }
26166     },
26167     'SELECT' : {
26168         name : {
26169             title: "name",
26170             width: 120
26171         },
26172         selectoptions : {
26173             title: "Options",
26174             width: 200
26175         }
26176     },
26177     
26178     // should we really allow this??
26179     // should this just be 
26180     'BODY' : {
26181         title : {
26182             title: "title",
26183             width: 200,
26184             disabled : true
26185         }
26186     },
26187     '*' : {
26188         // empty..
26189     }
26190 };
26191
26192
26193
26194 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26195     
26196     tb: false,
26197     
26198     rendered: false,
26199     
26200     editor : false,
26201     /**
26202      * @cfg {Object} disable  List of toolbar elements to disable
26203          
26204      */
26205     disable : false,
26206     /**
26207      * @cfg {Object} styles List of styles 
26208      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26209      *
26210      * These must be defined in the page, so they get rendered correctly..
26211      * .headline { }
26212      * TD.underline { }
26213      * 
26214      */
26215     styles : false,
26216     
26217     
26218     
26219     toolbars : false,
26220     
26221     init : function(editor)
26222     {
26223         this.editor = editor;
26224         
26225         
26226         var fid = editor.frameId;
26227         var etb = this;
26228         function btn(id, toggle, handler){
26229             var xid = fid + '-'+ id ;
26230             return {
26231                 id : xid,
26232                 cmd : id,
26233                 cls : 'x-btn-icon x-edit-'+id,
26234                 enableToggle:toggle !== false,
26235                 scope: editor, // was editor...
26236                 handler:handler||editor.relayBtnCmd,
26237                 clickEvent:'mousedown',
26238                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26239                 tabIndex:-1
26240             };
26241         }
26242         // create a new element.
26243         var wdiv = editor.wrap.createChild({
26244                 tag: 'div'
26245             }, editor.wrap.dom.firstChild.nextSibling, true);
26246         
26247         // can we do this more than once??
26248         
26249          // stop form submits
26250       
26251  
26252         // disable everything...
26253         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26254         this.toolbars = {};
26255            
26256         for (var i in  ty) {
26257           
26258             this.toolbars[i] = this.buildToolbar(ty[i],i);
26259         }
26260         this.tb = this.toolbars.BODY;
26261         this.tb.el.show();
26262         this.buildFooter();
26263         this.footer.show();
26264          
26265         this.rendered = true;
26266         
26267         // the all the btns;
26268         editor.on('editorevent', this.updateToolbar, this);
26269         // other toolbars need to implement this..
26270         //editor.on('editmodechange', this.updateToolbar, this);
26271     },
26272     
26273     
26274     
26275     /**
26276      * Protected method that will not generally be called directly. It triggers
26277      * a toolbar update by reading the markup state of the current selection in the editor.
26278      */
26279     updateToolbar: function(ignore_a,ignore_b,sel){
26280
26281         
26282         if(!this.editor.activated){
26283              this.editor.onFirstFocus();
26284             return;
26285         }
26286         var updateFooter = sel ? false : true;
26287         
26288         
26289         var ans = this.editor.getAllAncestors();
26290         
26291         // pick
26292         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26293         
26294         if (!sel) { 
26295             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26296             sel = sel ? sel : this.editor.doc.body;
26297             sel = sel.tagName.length ? sel : this.editor.doc.body;
26298             
26299         }
26300         // pick a menu that exists..
26301         var tn = sel.tagName.toUpperCase();
26302         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26303         
26304         tn = sel.tagName.toUpperCase();
26305         
26306         var lastSel = this.tb.selectedNode
26307         
26308         this.tb.selectedNode = sel;
26309         
26310         // if current menu does not match..
26311         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26312                 
26313             this.tb.el.hide();
26314             ///console.log("show: " + tn);
26315             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26316             this.tb.el.show();
26317             // update name
26318             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26319             
26320             
26321             // update attributes
26322             if (this.tb.fields) {
26323                 this.tb.fields.each(function(e) {
26324                    e.setValue(sel.getAttribute(e.name));
26325                 });
26326             }
26327             
26328             // update styles
26329             var st = this.tb.fields.item(0);
26330             st.store.removeAll();
26331             var cn = sel.className.split(/\s+/);
26332             
26333             var avs = [];
26334             if (this.styles['*']) {
26335                 
26336                 Roo.each(this.styles['*'], function(v) {
26337                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26338                 });
26339             }
26340             if (this.styles[tn]) { 
26341                 Roo.each(this.styles[tn], function(v) {
26342                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26343                 });
26344             }
26345             
26346             st.store.loadData(avs);
26347             st.collapse();
26348             st.setValue(cn);
26349             
26350             // flag our selected Node.
26351             this.tb.selectedNode = sel;
26352            
26353            
26354             Roo.menu.MenuMgr.hideAll();
26355
26356         }
26357         
26358         if (!updateFooter) {
26359             return;
26360         }
26361         // update the footer
26362         //
26363         var html = '';
26364         
26365         this.footerEls = ans.reverse();
26366         Roo.each(this.footerEls, function(a,i) {
26367             if (!a) { return; }
26368             html += html.length ? ' &gt; '  :  '';
26369             
26370             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26371             
26372         });
26373        
26374         // 
26375         var sz = this.footDisp.up('td').getSize();
26376         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26377         this.footDisp.dom.style.marginLeft = '5px';
26378         
26379         this.footDisp.dom.style.overflow = 'hidden';
26380         
26381         this.footDisp.dom.innerHTML = html;
26382             
26383         //this.editorsyncValue();
26384     },
26385    
26386        
26387     // private
26388     onDestroy : function(){
26389         if(this.rendered){
26390             
26391             this.tb.items.each(function(item){
26392                 if(item.menu){
26393                     item.menu.removeAll();
26394                     if(item.menu.el){
26395                         item.menu.el.destroy();
26396                     }
26397                 }
26398                 item.destroy();
26399             });
26400              
26401         }
26402     },
26403     onFirstFocus: function() {
26404         // need to do this for all the toolbars..
26405         this.tb.items.each(function(item){
26406            item.enable();
26407         });
26408     },
26409     buildToolbar: function(tlist, nm)
26410     {
26411         var editor = this.editor;
26412          // create a new element.
26413         var wdiv = editor.wrap.createChild({
26414                 tag: 'div'
26415             }, editor.wrap.dom.firstChild.nextSibling, true);
26416         
26417        
26418         var tb = new Roo.Toolbar(wdiv);
26419         // add the name..
26420         
26421         tb.add(nm+ ":&nbsp;");
26422         
26423         // styles...
26424         if (this.styles) {
26425             
26426             // this needs a multi-select checkbox...
26427             tb.addField( new Roo.form.ComboBox({
26428                 store: new Roo.data.SimpleStore({
26429                     id : 'val',
26430                     fields: ['val', 'selected'],
26431                     data : [] 
26432                 }),
26433                 name : 'className',
26434                 displayField:'val',
26435                 typeAhead: false,
26436                 mode: 'local',
26437                 editable : false,
26438                 triggerAction: 'all',
26439                 emptyText:'Select Style',
26440                 selectOnFocus:true,
26441                 width: 130,
26442                 listeners : {
26443                     'select': function(c, r, i) {
26444                         // initial support only for on class per el..
26445                         tb.selectedNode.className =  r ? r.get('val') : '';
26446                     }
26447                 }
26448     
26449             }));
26450         }
26451             
26452         
26453         
26454         for (var i in tlist) {
26455             
26456             var item = tlist[i];
26457             tb.add(item.title + ":&nbsp;");
26458             
26459             
26460             
26461             
26462             if (item.opts) {
26463                 // opts == pulldown..
26464                 tb.addField( new Roo.form.ComboBox({
26465                     store: new Roo.data.SimpleStore({
26466                         id : 'val',
26467                         fields: ['val'],
26468                         data : item.opts  
26469                     }),
26470                     name : i,
26471                     displayField:'val',
26472                     typeAhead: false,
26473                     mode: 'local',
26474                     editable : false,
26475                     triggerAction: 'all',
26476                     emptyText:'Select',
26477                     selectOnFocus:true,
26478                     width: item.width ? item.width  : 130,
26479                     listeners : {
26480                         'select': function(c, r, i) {
26481                             tb.selectedNode.setAttribute(c.name, r.get('val'));
26482                         }
26483                     }
26484
26485                 }));
26486                 continue;
26487                     
26488                  
26489                 
26490                 tb.addField( new Roo.form.TextField({
26491                     name: i,
26492                     width: 100,
26493                     //allowBlank:false,
26494                     value: ''
26495                 }));
26496                 continue;
26497             }
26498             tb.addField( new Roo.form.TextField({
26499                 name: i,
26500                 width: item.width,
26501                 //allowBlank:true,
26502                 value: '',
26503                 listeners: {
26504                     'change' : function(f, nv, ov) {
26505                         tb.selectedNode.setAttribute(f.name, nv);
26506                     }
26507                 }
26508             }));
26509              
26510         }
26511         tb.el.on('click', function(e){
26512             e.preventDefault(); // what does this do?
26513         });
26514         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
26515         tb.el.hide();
26516         tb.name = nm;
26517         // dont need to disable them... as they will get hidden
26518         return tb;
26519          
26520         
26521     },
26522     buildFooter : function()
26523     {
26524         
26525         var fel = this.editor.wrap.createChild();
26526         this.footer = new Roo.Toolbar(fel);
26527         // toolbar has scrolly on left / right?
26528         var footDisp= new Roo.Toolbar.Fill();
26529         var _t = this;
26530         this.footer.add(
26531             {
26532                 text : '&lt;',
26533                 xtype: 'Button',
26534                 handler : function() {
26535                     _t.footDisp.scrollTo('left',0,true)
26536                 }
26537             }
26538         );
26539         this.footer.add( footDisp );
26540         this.footer.add( 
26541             {
26542                 text : '&gt;',
26543                 xtype: 'Button',
26544                 handler : function() {
26545                     // no animation..
26546                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
26547                 }
26548             }
26549         );
26550         var fel = Roo.get(footDisp.el);
26551         fel.addClass('x-editor-context');
26552         this.footDispWrap = fel; 
26553         this.footDispWrap.overflow  = 'hidden';
26554         
26555         this.footDisp = fel.createChild();
26556         this.footDispWrap.on('click', this.onContextClick, this)
26557         
26558         
26559     },
26560     onContextClick : function (ev,dom)
26561     {
26562         ev.preventDefault();
26563         var  cn = dom.className;
26564         Roo.log(cn);
26565         if (!cn.match(/x-ed-loc-/)) {
26566             return;
26567         }
26568         var n = cn.split('-').pop();
26569         var ans = this.footerEls;
26570         var sel = ans[n];
26571         
26572          // pick
26573         var range = this.editor.createRange();
26574         
26575         range.selectNodeContents(sel);
26576         //range.selectNode(sel);
26577         
26578         
26579         var selection = this.editor.getSelection();
26580         selection.removeAllRanges();
26581         selection.addRange(range);
26582         
26583         
26584         
26585         this.updateToolbar(null, null, sel);
26586         
26587         
26588     }
26589     
26590     
26591     
26592     
26593     
26594 });
26595
26596
26597
26598
26599
26600 /*
26601  * Based on:
26602  * Ext JS Library 1.1.1
26603  * Copyright(c) 2006-2007, Ext JS, LLC.
26604  *
26605  * Originally Released Under LGPL - original licence link has changed is not relivant.
26606  *
26607  * Fork - LGPL
26608  * <script type="text/javascript">
26609  */
26610  
26611 /**
26612  * @class Roo.form.BasicForm
26613  * @extends Roo.util.Observable
26614  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
26615  * @constructor
26616  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26617  * @param {Object} config Configuration options
26618  */
26619 Roo.form.BasicForm = function(el, config){
26620     this.allItems = [];
26621     this.childForms = [];
26622     Roo.apply(this, config);
26623     /*
26624      * The Roo.form.Field items in this form.
26625      * @type MixedCollection
26626      */
26627      
26628      
26629     this.items = new Roo.util.MixedCollection(false, function(o){
26630         return o.id || (o.id = Roo.id());
26631     });
26632     this.addEvents({
26633         /**
26634          * @event beforeaction
26635          * Fires before any action is performed. Return false to cancel the action.
26636          * @param {Form} this
26637          * @param {Action} action The action to be performed
26638          */
26639         beforeaction: true,
26640         /**
26641          * @event actionfailed
26642          * Fires when an action fails.
26643          * @param {Form} this
26644          * @param {Action} action The action that failed
26645          */
26646         actionfailed : true,
26647         /**
26648          * @event actioncomplete
26649          * Fires when an action is completed.
26650          * @param {Form} this
26651          * @param {Action} action The action that completed
26652          */
26653         actioncomplete : true
26654     });
26655     if(el){
26656         this.initEl(el);
26657     }
26658     Roo.form.BasicForm.superclass.constructor.call(this);
26659 };
26660
26661 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26662     /**
26663      * @cfg {String} method
26664      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26665      */
26666     /**
26667      * @cfg {DataReader} reader
26668      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26669      * This is optional as there is built-in support for processing JSON.
26670      */
26671     /**
26672      * @cfg {DataReader} errorReader
26673      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26674      * This is completely optional as there is built-in support for processing JSON.
26675      */
26676     /**
26677      * @cfg {String} url
26678      * The URL to use for form actions if one isn't supplied in the action options.
26679      */
26680     /**
26681      * @cfg {Boolean} fileUpload
26682      * Set to true if this form is a file upload.
26683      */
26684      
26685     /**
26686      * @cfg {Object} baseParams
26687      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26688      */
26689      /**
26690      
26691     /**
26692      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26693      */
26694     timeout: 30,
26695
26696     // private
26697     activeAction : null,
26698
26699     /**
26700      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26701      * or setValues() data instead of when the form was first created.
26702      */
26703     trackResetOnLoad : false,
26704     
26705     
26706     /**
26707      * childForms - used for multi-tab forms
26708      * @type {Array}
26709      */
26710     childForms : false,
26711     
26712     /**
26713      * allItems - full list of fields.
26714      * @type {Array}
26715      */
26716     allItems : false,
26717     
26718     /**
26719      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26720      * element by passing it or its id or mask the form itself by passing in true.
26721      * @type Mixed
26722      */
26723     waitMsgTarget : false,
26724
26725     // private
26726     initEl : function(el){
26727         this.el = Roo.get(el);
26728         this.id = this.el.id || Roo.id();
26729         this.el.on('submit', this.onSubmit, this);
26730         this.el.addClass('x-form');
26731     },
26732
26733     // private
26734     onSubmit : function(e){
26735         e.stopEvent();
26736     },
26737
26738     /**
26739      * Returns true if client-side validation on the form is successful.
26740      * @return Boolean
26741      */
26742     isValid : function(){
26743         var valid = true;
26744         this.items.each(function(f){
26745            if(!f.validate()){
26746                valid = false;
26747            }
26748         });
26749         return valid;
26750     },
26751
26752     /**
26753      * Returns true if any fields in this form have changed since their original load.
26754      * @return Boolean
26755      */
26756     isDirty : function(){
26757         var dirty = false;
26758         this.items.each(function(f){
26759            if(f.isDirty()){
26760                dirty = true;
26761                return false;
26762            }
26763         });
26764         return dirty;
26765     },
26766
26767     /**
26768      * Performs a predefined action (submit or load) or custom actions you define on this form.
26769      * @param {String} actionName The name of the action type
26770      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26771      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26772      * accept other config options):
26773      * <pre>
26774 Property          Type             Description
26775 ----------------  ---------------  ----------------------------------------------------------------------------------
26776 url               String           The url for the action (defaults to the form's url)
26777 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26778 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26779 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26780                                    validate the form on the client (defaults to false)
26781      * </pre>
26782      * @return {BasicForm} this
26783      */
26784     doAction : function(action, options){
26785         if(typeof action == 'string'){
26786             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26787         }
26788         if(this.fireEvent('beforeaction', this, action) !== false){
26789             this.beforeAction(action);
26790             action.run.defer(100, action);
26791         }
26792         return this;
26793     },
26794
26795     /**
26796      * Shortcut to do a submit action.
26797      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26798      * @return {BasicForm} this
26799      */
26800     submit : function(options){
26801         this.doAction('submit', options);
26802         return this;
26803     },
26804
26805     /**
26806      * Shortcut to do a load action.
26807      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26808      * @return {BasicForm} this
26809      */
26810     load : function(options){
26811         this.doAction('load', options);
26812         return this;
26813     },
26814
26815     /**
26816      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26817      * @param {Record} record The record to edit
26818      * @return {BasicForm} this
26819      */
26820     updateRecord : function(record){
26821         record.beginEdit();
26822         var fs = record.fields;
26823         fs.each(function(f){
26824             var field = this.findField(f.name);
26825             if(field){
26826                 record.set(f.name, field.getValue());
26827             }
26828         }, this);
26829         record.endEdit();
26830         return this;
26831     },
26832
26833     /**
26834      * Loads an Roo.data.Record into this form.
26835      * @param {Record} record The record to load
26836      * @return {BasicForm} this
26837      */
26838     loadRecord : function(record){
26839         this.setValues(record.data);
26840         return this;
26841     },
26842
26843     // private
26844     beforeAction : function(action){
26845         var o = action.options;
26846         
26847        
26848         if(this.waitMsgTarget === true){
26849             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26850         }else if(this.waitMsgTarget){
26851             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26852             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26853         }else {
26854             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26855         }
26856          
26857     },
26858
26859     // private
26860     afterAction : function(action, success){
26861         this.activeAction = null;
26862         var o = action.options;
26863         
26864         if(this.waitMsgTarget === true){
26865             this.el.unmask();
26866         }else if(this.waitMsgTarget){
26867             this.waitMsgTarget.unmask();
26868         }else{
26869             Roo.MessageBox.updateProgress(1);
26870             Roo.MessageBox.hide();
26871         }
26872          
26873         if(success){
26874             if(o.reset){
26875                 this.reset();
26876             }
26877             Roo.callback(o.success, o.scope, [this, action]);
26878             this.fireEvent('actioncomplete', this, action);
26879             
26880         }else{
26881             Roo.callback(o.failure, o.scope, [this, action]);
26882             // show an error message if no failed handler is set..
26883             if (!this.hasListener('actionfailed')) {
26884                 Roo.MessageBox.alert("Error",
26885                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26886                         action.result.errorMsg :
26887                         "Saving Failed, please check your entries"
26888                 );
26889             }
26890             
26891             this.fireEvent('actionfailed', this, action);
26892         }
26893         
26894     },
26895
26896     /**
26897      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26898      * @param {String} id The value to search for
26899      * @return Field
26900      */
26901     findField : function(id){
26902         var field = this.items.get(id);
26903         if(!field){
26904             this.items.each(function(f){
26905                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26906                     field = f;
26907                     return false;
26908                 }
26909             });
26910         }
26911         return field || null;
26912     },
26913
26914     /**
26915      * Add a secondary form to this one, 
26916      * Used to provide tabbed forms. One form is primary, with hidden values 
26917      * which mirror the elements from the other forms.
26918      * 
26919      * @param {Roo.form.Form} form to add.
26920      * 
26921      */
26922     addForm : function(form)
26923     {
26924        
26925         if (this.childForms.indexOf(form) > -1) {
26926             // already added..
26927             return;
26928         }
26929         this.childForms.push(form);
26930         var n = '';
26931         Roo.each(form.allItems, function (fe) {
26932             
26933             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26934             if (this.findField(n)) { // already added..
26935                 return;
26936             }
26937             var add = new Roo.form.Hidden({
26938                 name : n
26939             });
26940             add.render(this.el);
26941             
26942             this.add( add );
26943         }, this);
26944         
26945     },
26946     /**
26947      * Mark fields in this form invalid in bulk.
26948      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26949      * @return {BasicForm} this
26950      */
26951     markInvalid : function(errors){
26952         if(errors instanceof Array){
26953             for(var i = 0, len = errors.length; i < len; i++){
26954                 var fieldError = errors[i];
26955                 var f = this.findField(fieldError.id);
26956                 if(f){
26957                     f.markInvalid(fieldError.msg);
26958                 }
26959             }
26960         }else{
26961             var field, id;
26962             for(id in errors){
26963                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26964                     field.markInvalid(errors[id]);
26965                 }
26966             }
26967         }
26968         Roo.each(this.childForms || [], function (f) {
26969             f.markInvalid(errors);
26970         });
26971         
26972         return this;
26973     },
26974
26975     /**
26976      * Set values for fields in this form in bulk.
26977      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26978      * @return {BasicForm} this
26979      */
26980     setValues : function(values){
26981         if(values instanceof Array){ // array of objects
26982             for(var i = 0, len = values.length; i < len; i++){
26983                 var v = values[i];
26984                 var f = this.findField(v.id);
26985                 if(f){
26986                     f.setValue(v.value);
26987                     if(this.trackResetOnLoad){
26988                         f.originalValue = f.getValue();
26989                     }
26990                 }
26991             }
26992         }else{ // object hash
26993             var field, id;
26994             for(id in values){
26995                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26996                     
26997                     if (field.setFromData && 
26998                         field.valueField && 
26999                         field.displayField &&
27000                         // combos' with local stores can 
27001                         // be queried via setValue()
27002                         // to set their value..
27003                         (field.store && !field.store.isLocal)
27004                         ) {
27005                         // it's a combo
27006                         var sd = { };
27007                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27008                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27009                         field.setFromData(sd);
27010                         
27011                     } else {
27012                         field.setValue(values[id]);
27013                     }
27014                     
27015                     
27016                     if(this.trackResetOnLoad){
27017                         field.originalValue = field.getValue();
27018                     }
27019                 }
27020             }
27021         }
27022          
27023         Roo.each(this.childForms || [], function (f) {
27024             f.setValues(values);
27025         });
27026                 
27027         return this;
27028     },
27029
27030     /**
27031      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27032      * they are returned as an array.
27033      * @param {Boolean} asString
27034      * @return {Object}
27035      */
27036     getValues : function(asString){
27037         if (this.childForms) {
27038             // copy values from the child forms
27039             Roo.each(this.childForms, function (f) {
27040                 this.setValues(f.getValues());
27041             }, this);
27042         }
27043         
27044         
27045         
27046         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27047         if(asString === true){
27048             return fs;
27049         }
27050         return Roo.urlDecode(fs);
27051     },
27052     
27053     /**
27054      * Returns the fields in this form as an object with key/value pairs. 
27055      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27056      * @return {Object}
27057      */
27058     getFieldValues : function(with_hidden)
27059     {
27060         if (this.childForms) {
27061             // copy values from the child forms
27062             // should this call getFieldValues - probably not as we do not currently copy
27063             // hidden fields when we generate..
27064             Roo.each(this.childForms, function (f) {
27065                 this.setValues(f.getValues());
27066             }, this);
27067         }
27068         
27069         var ret = {};
27070         this.items.each(function(f){
27071             if (!f.getName()) {
27072                 return;
27073             }
27074             var v = f.getValue();
27075             // not sure if this supported any more..
27076             if ((typeof(v) == 'object') && f.getRawValue) {
27077                 v = f.getRawValue() ; // dates..
27078             }
27079             // combo boxes where name != hiddenName...
27080             if (f.name != f.getName()) {
27081                 ret[f.name] = f.getRawValue();
27082             }
27083             ret[f.getName()] = v;
27084         });
27085         
27086         return ret;
27087     },
27088
27089     /**
27090      * Clears all invalid messages in this form.
27091      * @return {BasicForm} this
27092      */
27093     clearInvalid : function(){
27094         this.items.each(function(f){
27095            f.clearInvalid();
27096         });
27097         
27098         Roo.each(this.childForms || [], function (f) {
27099             f.clearInvalid();
27100         });
27101         
27102         
27103         return this;
27104     },
27105
27106     /**
27107      * Resets this form.
27108      * @return {BasicForm} this
27109      */
27110     reset : function(){
27111         this.items.each(function(f){
27112             f.reset();
27113         });
27114         
27115         Roo.each(this.childForms || [], function (f) {
27116             f.reset();
27117         });
27118        
27119         
27120         return this;
27121     },
27122
27123     /**
27124      * Add Roo.form components to this form.
27125      * @param {Field} field1
27126      * @param {Field} field2 (optional)
27127      * @param {Field} etc (optional)
27128      * @return {BasicForm} this
27129      */
27130     add : function(){
27131         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27132         return this;
27133     },
27134
27135
27136     /**
27137      * Removes a field from the items collection (does NOT remove its markup).
27138      * @param {Field} field
27139      * @return {BasicForm} this
27140      */
27141     remove : function(field){
27142         this.items.remove(field);
27143         return this;
27144     },
27145
27146     /**
27147      * Looks at the fields in this form, checks them for an id attribute,
27148      * and calls applyTo on the existing dom element with that id.
27149      * @return {BasicForm} this
27150      */
27151     render : function(){
27152         this.items.each(function(f){
27153             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27154                 f.applyTo(f.id);
27155             }
27156         });
27157         return this;
27158     },
27159
27160     /**
27161      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27162      * @param {Object} values
27163      * @return {BasicForm} this
27164      */
27165     applyToFields : function(o){
27166         this.items.each(function(f){
27167            Roo.apply(f, o);
27168         });
27169         return this;
27170     },
27171
27172     /**
27173      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27174      * @param {Object} values
27175      * @return {BasicForm} this
27176      */
27177     applyIfToFields : function(o){
27178         this.items.each(function(f){
27179            Roo.applyIf(f, o);
27180         });
27181         return this;
27182     }
27183 });
27184
27185 // back compat
27186 Roo.BasicForm = Roo.form.BasicForm;/*
27187  * Based on:
27188  * Ext JS Library 1.1.1
27189  * Copyright(c) 2006-2007, Ext JS, LLC.
27190  *
27191  * Originally Released Under LGPL - original licence link has changed is not relivant.
27192  *
27193  * Fork - LGPL
27194  * <script type="text/javascript">
27195  */
27196
27197 /**
27198  * @class Roo.form.Form
27199  * @extends Roo.form.BasicForm
27200  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27201  * @constructor
27202  * @param {Object} config Configuration options
27203  */
27204 Roo.form.Form = function(config){
27205     var xitems =  [];
27206     if (config.items) {
27207         xitems = config.items;
27208         delete config.items;
27209     }
27210    
27211     
27212     Roo.form.Form.superclass.constructor.call(this, null, config);
27213     this.url = this.url || this.action;
27214     if(!this.root){
27215         this.root = new Roo.form.Layout(Roo.applyIf({
27216             id: Roo.id()
27217         }, config));
27218     }
27219     this.active = this.root;
27220     /**
27221      * Array of all the buttons that have been added to this form via {@link addButton}
27222      * @type Array
27223      */
27224     this.buttons = [];
27225     this.allItems = [];
27226     this.addEvents({
27227         /**
27228          * @event clientvalidation
27229          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27230          * @param {Form} this
27231          * @param {Boolean} valid true if the form has passed client-side validation
27232          */
27233         clientvalidation: true,
27234         /**
27235          * @event rendered
27236          * Fires when the form is rendered
27237          * @param {Roo.form.Form} form
27238          */
27239         rendered : true
27240     });
27241     
27242     if (this.progressUrl) {
27243             // push a hidden field onto the list of fields..
27244             this.addxtype( {
27245                     xns: Roo.form, 
27246                     xtype : 'Hidden', 
27247                     name : 'UPLOAD_IDENTIFIER' 
27248             });
27249         }
27250         
27251     
27252     Roo.each(xitems, this.addxtype, this);
27253     
27254     
27255     
27256 };
27257
27258 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27259     /**
27260      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27261      */
27262     /**
27263      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27264      */
27265     /**
27266      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27267      */
27268     buttonAlign:'center',
27269
27270     /**
27271      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27272      */
27273     minButtonWidth:75,
27274
27275     /**
27276      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27277      * This property cascades to child containers if not set.
27278      */
27279     labelAlign:'left',
27280
27281     /**
27282      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27283      * fires a looping event with that state. This is required to bind buttons to the valid
27284      * state using the config value formBind:true on the button.
27285      */
27286     monitorValid : false,
27287
27288     /**
27289      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27290      */
27291     monitorPoll : 200,
27292     
27293     /**
27294      * @cfg {String} progressUrl - Url to return progress data 
27295      */
27296     
27297     progressUrl : false,
27298   
27299     /**
27300      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27301      * fields are added and the column is closed. If no fields are passed the column remains open
27302      * until end() is called.
27303      * @param {Object} config The config to pass to the column
27304      * @param {Field} field1 (optional)
27305      * @param {Field} field2 (optional)
27306      * @param {Field} etc (optional)
27307      * @return Column The column container object
27308      */
27309     column : function(c){
27310         var col = new Roo.form.Column(c);
27311         this.start(col);
27312         if(arguments.length > 1){ // duplicate code required because of Opera
27313             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27314             this.end();
27315         }
27316         return col;
27317     },
27318
27319     /**
27320      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27321      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27322      * until end() is called.
27323      * @param {Object} config The config to pass to the fieldset
27324      * @param {Field} field1 (optional)
27325      * @param {Field} field2 (optional)
27326      * @param {Field} etc (optional)
27327      * @return FieldSet The fieldset container object
27328      */
27329     fieldset : function(c){
27330         var fs = new Roo.form.FieldSet(c);
27331         this.start(fs);
27332         if(arguments.length > 1){ // duplicate code required because of Opera
27333             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27334             this.end();
27335         }
27336         return fs;
27337     },
27338
27339     /**
27340      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27341      * fields are added and the container is closed. If no fields are passed the container remains open
27342      * until end() is called.
27343      * @param {Object} config The config to pass to the Layout
27344      * @param {Field} field1 (optional)
27345      * @param {Field} field2 (optional)
27346      * @param {Field} etc (optional)
27347      * @return Layout The container object
27348      */
27349     container : function(c){
27350         var l = new Roo.form.Layout(c);
27351         this.start(l);
27352         if(arguments.length > 1){ // duplicate code required because of Opera
27353             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27354             this.end();
27355         }
27356         return l;
27357     },
27358
27359     /**
27360      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27361      * @param {Object} container A Roo.form.Layout or subclass of Layout
27362      * @return {Form} this
27363      */
27364     start : function(c){
27365         // cascade label info
27366         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27367         this.active.stack.push(c);
27368         c.ownerCt = this.active;
27369         this.active = c;
27370         return this;
27371     },
27372
27373     /**
27374      * Closes the current open container
27375      * @return {Form} this
27376      */
27377     end : function(){
27378         if(this.active == this.root){
27379             return this;
27380         }
27381         this.active = this.active.ownerCt;
27382         return this;
27383     },
27384
27385     /**
27386      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27387      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27388      * as the label of the field.
27389      * @param {Field} field1
27390      * @param {Field} field2 (optional)
27391      * @param {Field} etc. (optional)
27392      * @return {Form} this
27393      */
27394     add : function(){
27395         this.active.stack.push.apply(this.active.stack, arguments);
27396         this.allItems.push.apply(this.allItems,arguments);
27397         var r = [];
27398         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27399             if(a[i].isFormField){
27400                 r.push(a[i]);
27401             }
27402         }
27403         if(r.length > 0){
27404             Roo.form.Form.superclass.add.apply(this, r);
27405         }
27406         return this;
27407     },
27408     
27409
27410     
27411     
27412     
27413      /**
27414      * Find any element that has been added to a form, using it's ID or name
27415      * This can include framesets, columns etc. along with regular fields..
27416      * @param {String} id - id or name to find.
27417      
27418      * @return {Element} e - or false if nothing found.
27419      */
27420     findbyId : function(id)
27421     {
27422         var ret = false;
27423         if (!id) {
27424             return ret;
27425         }
27426         Roo.each(this.allItems, function(f){
27427             if (f.id == id || f.name == id ){
27428                 ret = f;
27429                 return false;
27430             }
27431         });
27432         return ret;
27433     },
27434
27435     
27436     
27437     /**
27438      * Render this form into the passed container. This should only be called once!
27439      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27440      * @return {Form} this
27441      */
27442     render : function(ct)
27443     {
27444         
27445         
27446         
27447         ct = Roo.get(ct);
27448         var o = this.autoCreate || {
27449             tag: 'form',
27450             method : this.method || 'POST',
27451             id : this.id || Roo.id()
27452         };
27453         this.initEl(ct.createChild(o));
27454
27455         this.root.render(this.el);
27456         
27457        
27458              
27459         this.items.each(function(f){
27460             f.render('x-form-el-'+f.id);
27461         });
27462
27463         if(this.buttons.length > 0){
27464             // tables are required to maintain order and for correct IE layout
27465             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27466                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27467                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27468             }}, null, true);
27469             var tr = tb.getElementsByTagName('tr')[0];
27470             for(var i = 0, len = this.buttons.length; i < len; i++) {
27471                 var b = this.buttons[i];
27472                 var td = document.createElement('td');
27473                 td.className = 'x-form-btn-td';
27474                 b.render(tr.appendChild(td));
27475             }
27476         }
27477         if(this.monitorValid){ // initialize after render
27478             this.startMonitoring();
27479         }
27480         this.fireEvent('rendered', this);
27481         return this;
27482     },
27483
27484     /**
27485      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27486      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27487      * object or a valid Roo.DomHelper element config
27488      * @param {Function} handler The function called when the button is clicked
27489      * @param {Object} scope (optional) The scope of the handler function
27490      * @return {Roo.Button}
27491      */
27492     addButton : function(config, handler, scope){
27493         var bc = {
27494             handler: handler,
27495             scope: scope,
27496             minWidth: this.minButtonWidth,
27497             hideParent:true
27498         };
27499         if(typeof config == "string"){
27500             bc.text = config;
27501         }else{
27502             Roo.apply(bc, config);
27503         }
27504         var btn = new Roo.Button(null, bc);
27505         this.buttons.push(btn);
27506         return btn;
27507     },
27508
27509      /**
27510      * Adds a series of form elements (using the xtype property as the factory method.
27511      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27512      * @param {Object} config 
27513      */
27514     
27515     addxtype : function()
27516     {
27517         var ar = Array.prototype.slice.call(arguments, 0);
27518         var ret = false;
27519         for(var i = 0; i < ar.length; i++) {
27520             if (!ar[i]) {
27521                 continue; // skip -- if this happends something invalid got sent, we 
27522                 // should ignore it, as basically that interface element will not show up
27523                 // and that should be pretty obvious!!
27524             }
27525             
27526             if (Roo.form[ar[i].xtype]) {
27527                 ar[i].form = this;
27528                 var fe = Roo.factory(ar[i], Roo.form);
27529                 if (!ret) {
27530                     ret = fe;
27531                 }
27532                 fe.form = this;
27533                 if (fe.store) {
27534                     fe.store.form = this;
27535                 }
27536                 if (fe.isLayout) {  
27537                          
27538                     this.start(fe);
27539                     this.allItems.push(fe);
27540                     if (fe.items && fe.addxtype) {
27541                         fe.addxtype.apply(fe, fe.items);
27542                         delete fe.items;
27543                     }
27544                      this.end();
27545                     continue;
27546                 }
27547                 
27548                 
27549                  
27550                 this.add(fe);
27551               //  console.log('adding ' + ar[i].xtype);
27552             }
27553             if (ar[i].xtype == 'Button') {  
27554                 //console.log('adding button');
27555                 //console.log(ar[i]);
27556                 this.addButton(ar[i]);
27557                 this.allItems.push(fe);
27558                 continue;
27559             }
27560             
27561             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27562                 alert('end is not supported on xtype any more, use items');
27563             //    this.end();
27564             //    //console.log('adding end');
27565             }
27566             
27567         }
27568         return ret;
27569     },
27570     
27571     /**
27572      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27573      * option "monitorValid"
27574      */
27575     startMonitoring : function(){
27576         if(!this.bound){
27577             this.bound = true;
27578             Roo.TaskMgr.start({
27579                 run : this.bindHandler,
27580                 interval : this.monitorPoll || 200,
27581                 scope: this
27582             });
27583         }
27584     },
27585
27586     /**
27587      * Stops monitoring of the valid state of this form
27588      */
27589     stopMonitoring : function(){
27590         this.bound = false;
27591     },
27592
27593     // private
27594     bindHandler : function(){
27595         if(!this.bound){
27596             return false; // stops binding
27597         }
27598         var valid = true;
27599         this.items.each(function(f){
27600             if(!f.isValid(true)){
27601                 valid = false;
27602                 return false;
27603             }
27604         });
27605         for(var i = 0, len = this.buttons.length; i < len; i++){
27606             var btn = this.buttons[i];
27607             if(btn.formBind === true && btn.disabled === valid){
27608                 btn.setDisabled(!valid);
27609             }
27610         }
27611         this.fireEvent('clientvalidation', this, valid);
27612     }
27613     
27614     
27615     
27616     
27617     
27618     
27619     
27620     
27621 });
27622
27623
27624 // back compat
27625 Roo.Form = Roo.form.Form;
27626 /*
27627  * Based on:
27628  * Ext JS Library 1.1.1
27629  * Copyright(c) 2006-2007, Ext JS, LLC.
27630  *
27631  * Originally Released Under LGPL - original licence link has changed is not relivant.
27632  *
27633  * Fork - LGPL
27634  * <script type="text/javascript">
27635  */
27636  
27637  /**
27638  * @class Roo.form.Action
27639  * Internal Class used to handle form actions
27640  * @constructor
27641  * @param {Roo.form.BasicForm} el The form element or its id
27642  * @param {Object} config Configuration options
27643  */
27644  
27645  
27646 // define the action interface
27647 Roo.form.Action = function(form, options){
27648     this.form = form;
27649     this.options = options || {};
27650 };
27651 /**
27652  * Client Validation Failed
27653  * @const 
27654  */
27655 Roo.form.Action.CLIENT_INVALID = 'client';
27656 /**
27657  * Server Validation Failed
27658  * @const 
27659  */
27660  Roo.form.Action.SERVER_INVALID = 'server';
27661  /**
27662  * Connect to Server Failed
27663  * @const 
27664  */
27665 Roo.form.Action.CONNECT_FAILURE = 'connect';
27666 /**
27667  * Reading Data from Server Failed
27668  * @const 
27669  */
27670 Roo.form.Action.LOAD_FAILURE = 'load';
27671
27672 Roo.form.Action.prototype = {
27673     type : 'default',
27674     failureType : undefined,
27675     response : undefined,
27676     result : undefined,
27677
27678     // interface method
27679     run : function(options){
27680
27681     },
27682
27683     // interface method
27684     success : function(response){
27685
27686     },
27687
27688     // interface method
27689     handleResponse : function(response){
27690
27691     },
27692
27693     // default connection failure
27694     failure : function(response){
27695         
27696         this.response = response;
27697         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27698         this.form.afterAction(this, false);
27699     },
27700
27701     processResponse : function(response){
27702         this.response = response;
27703         if(!response.responseText){
27704             return true;
27705         }
27706         this.result = this.handleResponse(response);
27707         return this.result;
27708     },
27709
27710     // utility functions used internally
27711     getUrl : function(appendParams){
27712         var url = this.options.url || this.form.url || this.form.el.dom.action;
27713         if(appendParams){
27714             var p = this.getParams();
27715             if(p){
27716                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27717             }
27718         }
27719         return url;
27720     },
27721
27722     getMethod : function(){
27723         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27724     },
27725
27726     getParams : function(){
27727         var bp = this.form.baseParams;
27728         var p = this.options.params;
27729         if(p){
27730             if(typeof p == "object"){
27731                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27732             }else if(typeof p == 'string' && bp){
27733                 p += '&' + Roo.urlEncode(bp);
27734             }
27735         }else if(bp){
27736             p = Roo.urlEncode(bp);
27737         }
27738         return p;
27739     },
27740
27741     createCallback : function(){
27742         return {
27743             success: this.success,
27744             failure: this.failure,
27745             scope: this,
27746             timeout: (this.form.timeout*1000),
27747             upload: this.form.fileUpload ? this.success : undefined
27748         };
27749     }
27750 };
27751
27752 Roo.form.Action.Submit = function(form, options){
27753     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27754 };
27755
27756 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27757     type : 'submit',
27758
27759     haveProgress : false,
27760     uploadComplete : false,
27761     
27762     // uploadProgress indicator.
27763     uploadProgress : function()
27764     {
27765         if (!this.form.progressUrl) {
27766             return;
27767         }
27768         
27769         if (!this.haveProgress) {
27770             Roo.MessageBox.progress("Uploading", "Uploading");
27771         }
27772         if (this.uploadComplete) {
27773            Roo.MessageBox.hide();
27774            return;
27775         }
27776         
27777         this.haveProgress = true;
27778    
27779         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27780         
27781         var c = new Roo.data.Connection();
27782         c.request({
27783             url : this.form.progressUrl,
27784             params: {
27785                 id : uid
27786             },
27787             method: 'GET',
27788             success : function(req){
27789                //console.log(data);
27790                 var rdata = false;
27791                 var edata;
27792                 try  {
27793                    rdata = Roo.decode(req.responseText)
27794                 } catch (e) {
27795                     Roo.log("Invalid data from server..");
27796                     Roo.log(edata);
27797                     return;
27798                 }
27799                 if (!rdata || !rdata.success) {
27800                     Roo.log(rdata);
27801                     return;
27802                 }
27803                 var data = rdata.data;
27804                 
27805                 if (this.uploadComplete) {
27806                    Roo.MessageBox.hide();
27807                    return;
27808                 }
27809                    
27810                 if (data){
27811                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27812                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27813                     );
27814                 }
27815                 this.uploadProgress.defer(2000,this);
27816             },
27817        
27818             failure: function(data) {
27819                 Roo.log('progress url failed ');
27820                 Roo.log(data);
27821             },
27822             scope : this
27823         });
27824            
27825     },
27826     
27827     
27828     run : function()
27829     {
27830         // run get Values on the form, so it syncs any secondary forms.
27831         this.form.getValues();
27832         
27833         var o = this.options;
27834         var method = this.getMethod();
27835         var isPost = method == 'POST';
27836         if(o.clientValidation === false || this.form.isValid()){
27837             
27838             if (this.form.progressUrl) {
27839                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27840                     (new Date() * 1) + '' + Math.random());
27841                     
27842             } 
27843             
27844             
27845             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27846                 form:this.form.el.dom,
27847                 url:this.getUrl(!isPost),
27848                 method: method,
27849                 params:isPost ? this.getParams() : null,
27850                 isUpload: this.form.fileUpload
27851             }));
27852             
27853             this.uploadProgress();
27854
27855         }else if (o.clientValidation !== false){ // client validation failed
27856             this.failureType = Roo.form.Action.CLIENT_INVALID;
27857             this.form.afterAction(this, false);
27858         }
27859     },
27860
27861     success : function(response)
27862     {
27863         this.uploadComplete= true;
27864         if (this.haveProgress) {
27865             Roo.MessageBox.hide();
27866         }
27867         
27868         
27869         var result = this.processResponse(response);
27870         if(result === true || result.success){
27871             this.form.afterAction(this, true);
27872             return;
27873         }
27874         if(result.errors){
27875             this.form.markInvalid(result.errors);
27876             this.failureType = Roo.form.Action.SERVER_INVALID;
27877         }
27878         this.form.afterAction(this, false);
27879     },
27880     failure : function(response)
27881     {
27882         this.uploadComplete= true;
27883         if (this.haveProgress) {
27884             Roo.MessageBox.hide();
27885         }
27886         
27887         this.response = response;
27888         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27889         this.form.afterAction(this, false);
27890     },
27891     
27892     handleResponse : function(response){
27893         if(this.form.errorReader){
27894             var rs = this.form.errorReader.read(response);
27895             var errors = [];
27896             if(rs.records){
27897                 for(var i = 0, len = rs.records.length; i < len; i++) {
27898                     var r = rs.records[i];
27899                     errors[i] = r.data;
27900                 }
27901             }
27902             if(errors.length < 1){
27903                 errors = null;
27904             }
27905             return {
27906                 success : rs.success,
27907                 errors : errors
27908             };
27909         }
27910         var ret = false;
27911         try {
27912             ret = Roo.decode(response.responseText);
27913         } catch (e) {
27914             ret = {
27915                 success: false,
27916                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27917                 errors : []
27918             };
27919         }
27920         return ret;
27921         
27922     }
27923 });
27924
27925
27926 Roo.form.Action.Load = function(form, options){
27927     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27928     this.reader = this.form.reader;
27929 };
27930
27931 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27932     type : 'load',
27933
27934     run : function(){
27935         
27936         Roo.Ajax.request(Roo.apply(
27937                 this.createCallback(), {
27938                     method:this.getMethod(),
27939                     url:this.getUrl(false),
27940                     params:this.getParams()
27941         }));
27942     },
27943
27944     success : function(response){
27945         
27946         var result = this.processResponse(response);
27947         if(result === true || !result.success || !result.data){
27948             this.failureType = Roo.form.Action.LOAD_FAILURE;
27949             this.form.afterAction(this, false);
27950             return;
27951         }
27952         this.form.clearInvalid();
27953         this.form.setValues(result.data);
27954         this.form.afterAction(this, true);
27955     },
27956
27957     handleResponse : function(response){
27958         if(this.form.reader){
27959             var rs = this.form.reader.read(response);
27960             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27961             return {
27962                 success : rs.success,
27963                 data : data
27964             };
27965         }
27966         return Roo.decode(response.responseText);
27967     }
27968 });
27969
27970 Roo.form.Action.ACTION_TYPES = {
27971     'load' : Roo.form.Action.Load,
27972     'submit' : Roo.form.Action.Submit
27973 };/*
27974  * Based on:
27975  * Ext JS Library 1.1.1
27976  * Copyright(c) 2006-2007, Ext JS, LLC.
27977  *
27978  * Originally Released Under LGPL - original licence link has changed is not relivant.
27979  *
27980  * Fork - LGPL
27981  * <script type="text/javascript">
27982  */
27983  
27984 /**
27985  * @class Roo.form.Layout
27986  * @extends Roo.Component
27987  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27988  * @constructor
27989  * @param {Object} config Configuration options
27990  */
27991 Roo.form.Layout = function(config){
27992     var xitems = [];
27993     if (config.items) {
27994         xitems = config.items;
27995         delete config.items;
27996     }
27997     Roo.form.Layout.superclass.constructor.call(this, config);
27998     this.stack = [];
27999     Roo.each(xitems, this.addxtype, this);
28000      
28001 };
28002
28003 Roo.extend(Roo.form.Layout, Roo.Component, {
28004     /**
28005      * @cfg {String/Object} autoCreate
28006      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28007      */
28008     /**
28009      * @cfg {String/Object/Function} style
28010      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28011      * a function which returns such a specification.
28012      */
28013     /**
28014      * @cfg {String} labelAlign
28015      * Valid values are "left," "top" and "right" (defaults to "left")
28016      */
28017     /**
28018      * @cfg {Number} labelWidth
28019      * Fixed width in pixels of all field labels (defaults to undefined)
28020      */
28021     /**
28022      * @cfg {Boolean} clear
28023      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28024      */
28025     clear : true,
28026     /**
28027      * @cfg {String} labelSeparator
28028      * The separator to use after field labels (defaults to ':')
28029      */
28030     labelSeparator : ':',
28031     /**
28032      * @cfg {Boolean} hideLabels
28033      * True to suppress the display of field labels in this layout (defaults to false)
28034      */
28035     hideLabels : false,
28036
28037     // private
28038     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28039     
28040     isLayout : true,
28041     
28042     // private
28043     onRender : function(ct, position){
28044         if(this.el){ // from markup
28045             this.el = Roo.get(this.el);
28046         }else {  // generate
28047             var cfg = this.getAutoCreate();
28048             this.el = ct.createChild(cfg, position);
28049         }
28050         if(this.style){
28051             this.el.applyStyles(this.style);
28052         }
28053         if(this.labelAlign){
28054             this.el.addClass('x-form-label-'+this.labelAlign);
28055         }
28056         if(this.hideLabels){
28057             this.labelStyle = "display:none";
28058             this.elementStyle = "padding-left:0;";
28059         }else{
28060             if(typeof this.labelWidth == 'number'){
28061                 this.labelStyle = "width:"+this.labelWidth+"px;";
28062                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28063             }
28064             if(this.labelAlign == 'top'){
28065                 this.labelStyle = "width:auto;";
28066                 this.elementStyle = "padding-left:0;";
28067             }
28068         }
28069         var stack = this.stack;
28070         var slen = stack.length;
28071         if(slen > 0){
28072             if(!this.fieldTpl){
28073                 var t = new Roo.Template(
28074                     '<div class="x-form-item {5}">',
28075                         '<label for="{0}" style="{2}">{1}{4}</label>',
28076                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28077                         '</div>',
28078                     '</div><div class="x-form-clear-left"></div>'
28079                 );
28080                 t.disableFormats = true;
28081                 t.compile();
28082                 Roo.form.Layout.prototype.fieldTpl = t;
28083             }
28084             for(var i = 0; i < slen; i++) {
28085                 if(stack[i].isFormField){
28086                     this.renderField(stack[i]);
28087                 }else{
28088                     this.renderComponent(stack[i]);
28089                 }
28090             }
28091         }
28092         if(this.clear){
28093             this.el.createChild({cls:'x-form-clear'});
28094         }
28095     },
28096
28097     // private
28098     renderField : function(f){
28099         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28100                f.id, //0
28101                f.fieldLabel, //1
28102                f.labelStyle||this.labelStyle||'', //2
28103                this.elementStyle||'', //3
28104                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28105                f.itemCls||this.itemCls||''  //5
28106        ], true).getPrevSibling());
28107     },
28108
28109     // private
28110     renderComponent : function(c){
28111         c.render(c.isLayout ? this.el : this.el.createChild());    
28112     },
28113     /**
28114      * Adds a object form elements (using the xtype property as the factory method.)
28115      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28116      * @param {Object} config 
28117      */
28118     addxtype : function(o)
28119     {
28120         // create the lement.
28121         o.form = this.form;
28122         var fe = Roo.factory(o, Roo.form);
28123         this.form.allItems.push(fe);
28124         this.stack.push(fe);
28125         
28126         if (fe.isFormField) {
28127             this.form.items.add(fe);
28128         }
28129          
28130         return fe;
28131     }
28132 });
28133
28134 /**
28135  * @class Roo.form.Column
28136  * @extends Roo.form.Layout
28137  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28138  * @constructor
28139  * @param {Object} config Configuration options
28140  */
28141 Roo.form.Column = function(config){
28142     Roo.form.Column.superclass.constructor.call(this, config);
28143 };
28144
28145 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28146     /**
28147      * @cfg {Number/String} width
28148      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28149      */
28150     /**
28151      * @cfg {String/Object} autoCreate
28152      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28153      */
28154
28155     // private
28156     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28157
28158     // private
28159     onRender : function(ct, position){
28160         Roo.form.Column.superclass.onRender.call(this, ct, position);
28161         if(this.width){
28162             this.el.setWidth(this.width);
28163         }
28164     }
28165 });
28166
28167
28168 /**
28169  * @class Roo.form.Row
28170  * @extends Roo.form.Layout
28171  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28172  * @constructor
28173  * @param {Object} config Configuration options
28174  */
28175
28176  
28177 Roo.form.Row = function(config){
28178     Roo.form.Row.superclass.constructor.call(this, config);
28179 };
28180  
28181 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28182       /**
28183      * @cfg {Number/String} width
28184      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28185      */
28186     /**
28187      * @cfg {Number/String} height
28188      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28189      */
28190     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28191     
28192     padWidth : 20,
28193     // private
28194     onRender : function(ct, position){
28195         //console.log('row render');
28196         if(!this.rowTpl){
28197             var t = new Roo.Template(
28198                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28199                     '<label for="{0}" style="{2}">{1}{4}</label>',
28200                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28201                     '</div>',
28202                 '</div>'
28203             );
28204             t.disableFormats = true;
28205             t.compile();
28206             Roo.form.Layout.prototype.rowTpl = t;
28207         }
28208         this.fieldTpl = this.rowTpl;
28209         
28210         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28211         var labelWidth = 100;
28212         
28213         if ((this.labelAlign != 'top')) {
28214             if (typeof this.labelWidth == 'number') {
28215                 labelWidth = this.labelWidth
28216             }
28217             this.padWidth =  20 + labelWidth;
28218             
28219         }
28220         
28221         Roo.form.Column.superclass.onRender.call(this, ct, position);
28222         if(this.width){
28223             this.el.setWidth(this.width);
28224         }
28225         if(this.height){
28226             this.el.setHeight(this.height);
28227         }
28228     },
28229     
28230     // private
28231     renderField : function(f){
28232         f.fieldEl = this.fieldTpl.append(this.el, [
28233                f.id, f.fieldLabel,
28234                f.labelStyle||this.labelStyle||'',
28235                this.elementStyle||'',
28236                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28237                f.itemCls||this.itemCls||'',
28238                f.width ? f.width + this.padWidth : 160 + this.padWidth
28239        ],true);
28240     }
28241 });
28242  
28243
28244 /**
28245  * @class Roo.form.FieldSet
28246  * @extends Roo.form.Layout
28247  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28248  * @constructor
28249  * @param {Object} config Configuration options
28250  */
28251 Roo.form.FieldSet = function(config){
28252     Roo.form.FieldSet.superclass.constructor.call(this, config);
28253 };
28254
28255 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28256     /**
28257      * @cfg {String} legend
28258      * The text to display as the legend for the FieldSet (defaults to '')
28259      */
28260     /**
28261      * @cfg {String/Object} autoCreate
28262      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28263      */
28264
28265     // private
28266     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28267
28268     // private
28269     onRender : function(ct, position){
28270         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28271         if(this.legend){
28272             this.setLegend(this.legend);
28273         }
28274     },
28275
28276     // private
28277     setLegend : function(text){
28278         if(this.rendered){
28279             this.el.child('legend').update(text);
28280         }
28281     }
28282 });/*
28283  * Based on:
28284  * Ext JS Library 1.1.1
28285  * Copyright(c) 2006-2007, Ext JS, LLC.
28286  *
28287  * Originally Released Under LGPL - original licence link has changed is not relivant.
28288  *
28289  * Fork - LGPL
28290  * <script type="text/javascript">
28291  */
28292 /**
28293  * @class Roo.form.VTypes
28294  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28295  * @singleton
28296  */
28297 Roo.form.VTypes = function(){
28298     // closure these in so they are only created once.
28299     var alpha = /^[a-zA-Z_]+$/;
28300     var alphanum = /^[a-zA-Z0-9_]+$/;
28301     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28302     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28303
28304     // All these messages and functions are configurable
28305     return {
28306         /**
28307          * The function used to validate email addresses
28308          * @param {String} value The email address
28309          */
28310         'email' : function(v){
28311             return email.test(v);
28312         },
28313         /**
28314          * The error text to display when the email validation function returns false
28315          * @type String
28316          */
28317         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28318         /**
28319          * The keystroke filter mask to be applied on email input
28320          * @type RegExp
28321          */
28322         'emailMask' : /[a-z0-9_\.\-@]/i,
28323
28324         /**
28325          * The function used to validate URLs
28326          * @param {String} value The URL
28327          */
28328         'url' : function(v){
28329             return url.test(v);
28330         },
28331         /**
28332          * The error text to display when the url validation function returns false
28333          * @type String
28334          */
28335         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28336         
28337         /**
28338          * The function used to validate alpha values
28339          * @param {String} value The value
28340          */
28341         'alpha' : function(v){
28342             return alpha.test(v);
28343         },
28344         /**
28345          * The error text to display when the alpha validation function returns false
28346          * @type String
28347          */
28348         'alphaText' : 'This field should only contain letters and _',
28349         /**
28350          * The keystroke filter mask to be applied on alpha input
28351          * @type RegExp
28352          */
28353         'alphaMask' : /[a-z_]/i,
28354
28355         /**
28356          * The function used to validate alphanumeric values
28357          * @param {String} value The value
28358          */
28359         'alphanum' : function(v){
28360             return alphanum.test(v);
28361         },
28362         /**
28363          * The error text to display when the alphanumeric validation function returns false
28364          * @type String
28365          */
28366         'alphanumText' : 'This field should only contain letters, numbers and _',
28367         /**
28368          * The keystroke filter mask to be applied on alphanumeric input
28369          * @type RegExp
28370          */
28371         'alphanumMask' : /[a-z0-9_]/i
28372     };
28373 }();//<script type="text/javascript">
28374
28375 /**
28376  * @class Roo.form.FCKeditor
28377  * @extends Roo.form.TextArea
28378  * Wrapper around the FCKEditor http://www.fckeditor.net
28379  * @constructor
28380  * Creates a new FCKeditor
28381  * @param {Object} config Configuration options
28382  */
28383 Roo.form.FCKeditor = function(config){
28384     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28385     this.addEvents({
28386          /**
28387          * @event editorinit
28388          * Fired when the editor is initialized - you can add extra handlers here..
28389          * @param {FCKeditor} this
28390          * @param {Object} the FCK object.
28391          */
28392         editorinit : true
28393     });
28394     
28395     
28396 };
28397 Roo.form.FCKeditor.editors = { };
28398 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28399 {
28400     //defaultAutoCreate : {
28401     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28402     //},
28403     // private
28404     /**
28405      * @cfg {Object} fck options - see fck manual for details.
28406      */
28407     fckconfig : false,
28408     
28409     /**
28410      * @cfg {Object} fck toolbar set (Basic or Default)
28411      */
28412     toolbarSet : 'Basic',
28413     /**
28414      * @cfg {Object} fck BasePath
28415      */ 
28416     basePath : '/fckeditor/',
28417     
28418     
28419     frame : false,
28420     
28421     value : '',
28422     
28423    
28424     onRender : function(ct, position)
28425     {
28426         if(!this.el){
28427             this.defaultAutoCreate = {
28428                 tag: "textarea",
28429                 style:"width:300px;height:60px;",
28430                 autocomplete: "off"
28431             };
28432         }
28433         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28434         /*
28435         if(this.grow){
28436             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28437             if(this.preventScrollbars){
28438                 this.el.setStyle("overflow", "hidden");
28439             }
28440             this.el.setHeight(this.growMin);
28441         }
28442         */
28443         //console.log('onrender' + this.getId() );
28444         Roo.form.FCKeditor.editors[this.getId()] = this;
28445          
28446
28447         this.replaceTextarea() ;
28448         
28449     },
28450     
28451     getEditor : function() {
28452         return this.fckEditor;
28453     },
28454     /**
28455      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28456      * @param {Mixed} value The value to set
28457      */
28458     
28459     
28460     setValue : function(value)
28461     {
28462         //console.log('setValue: ' + value);
28463         
28464         if(typeof(value) == 'undefined') { // not sure why this is happending...
28465             return;
28466         }
28467         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28468         
28469         //if(!this.el || !this.getEditor()) {
28470         //    this.value = value;
28471             //this.setValue.defer(100,this,[value]);    
28472         //    return;
28473         //} 
28474         
28475         if(!this.getEditor()) {
28476             return;
28477         }
28478         
28479         this.getEditor().SetData(value);
28480         
28481         //
28482
28483     },
28484
28485     /**
28486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28487      * @return {Mixed} value The field value
28488      */
28489     getValue : function()
28490     {
28491         
28492         if (this.frame && this.frame.dom.style.display == 'none') {
28493             return Roo.form.FCKeditor.superclass.getValue.call(this);
28494         }
28495         
28496         if(!this.el || !this.getEditor()) {
28497            
28498            // this.getValue.defer(100,this); 
28499             return this.value;
28500         }
28501        
28502         
28503         var value=this.getEditor().GetData();
28504         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28505         return Roo.form.FCKeditor.superclass.getValue.call(this);
28506         
28507
28508     },
28509
28510     /**
28511      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28512      * @return {Mixed} value The field value
28513      */
28514     getRawValue : function()
28515     {
28516         if (this.frame && this.frame.dom.style.display == 'none') {
28517             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28518         }
28519         
28520         if(!this.el || !this.getEditor()) {
28521             //this.getRawValue.defer(100,this); 
28522             return this.value;
28523             return;
28524         }
28525         
28526         
28527         
28528         var value=this.getEditor().GetData();
28529         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28530         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28531          
28532     },
28533     
28534     setSize : function(w,h) {
28535         
28536         
28537         
28538         //if (this.frame && this.frame.dom.style.display == 'none') {
28539         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28540         //    return;
28541         //}
28542         //if(!this.el || !this.getEditor()) {
28543         //    this.setSize.defer(100,this, [w,h]); 
28544         //    return;
28545         //}
28546         
28547         
28548         
28549         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28550         
28551         this.frame.dom.setAttribute('width', w);
28552         this.frame.dom.setAttribute('height', h);
28553         this.frame.setSize(w,h);
28554         
28555     },
28556     
28557     toggleSourceEdit : function(value) {
28558         
28559       
28560          
28561         this.el.dom.style.display = value ? '' : 'none';
28562         this.frame.dom.style.display = value ?  'none' : '';
28563         
28564     },
28565     
28566     
28567     focus: function(tag)
28568     {
28569         if (this.frame.dom.style.display == 'none') {
28570             return Roo.form.FCKeditor.superclass.focus.call(this);
28571         }
28572         if(!this.el || !this.getEditor()) {
28573             this.focus.defer(100,this, [tag]); 
28574             return;
28575         }
28576         
28577         
28578         
28579         
28580         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28581         this.getEditor().Focus();
28582         if (tgs.length) {
28583             if (!this.getEditor().Selection.GetSelection()) {
28584                 this.focus.defer(100,this, [tag]); 
28585                 return;
28586             }
28587             
28588             
28589             var r = this.getEditor().EditorDocument.createRange();
28590             r.setStart(tgs[0],0);
28591             r.setEnd(tgs[0],0);
28592             this.getEditor().Selection.GetSelection().removeAllRanges();
28593             this.getEditor().Selection.GetSelection().addRange(r);
28594             this.getEditor().Focus();
28595         }
28596         
28597     },
28598     
28599     
28600     
28601     replaceTextarea : function()
28602     {
28603         if ( document.getElementById( this.getId() + '___Frame' ) )
28604             return ;
28605         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28606         //{
28607             // We must check the elements firstly using the Id and then the name.
28608         var oTextarea = document.getElementById( this.getId() );
28609         
28610         var colElementsByName = document.getElementsByName( this.getId() ) ;
28611          
28612         oTextarea.style.display = 'none' ;
28613
28614         if ( oTextarea.tabIndex ) {            
28615             this.TabIndex = oTextarea.tabIndex ;
28616         }
28617         
28618         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28619         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28620         this.frame = Roo.get(this.getId() + '___Frame')
28621     },
28622     
28623     _getConfigHtml : function()
28624     {
28625         var sConfig = '' ;
28626
28627         for ( var o in this.fckconfig ) {
28628             sConfig += sConfig.length > 0  ? '&amp;' : '';
28629             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28630         }
28631
28632         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28633     },
28634     
28635     
28636     _getIFrameHtml : function()
28637     {
28638         var sFile = 'fckeditor.html' ;
28639         /* no idea what this is about..
28640         try
28641         {
28642             if ( (/fcksource=true/i).test( window.top.location.search ) )
28643                 sFile = 'fckeditor.original.html' ;
28644         }
28645         catch (e) { 
28646         */
28647
28648         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28649         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28650         
28651         
28652         var html = '<iframe id="' + this.getId() +
28653             '___Frame" src="' + sLink +
28654             '" width="' + this.width +
28655             '" height="' + this.height + '"' +
28656             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28657             ' frameborder="0" scrolling="no"></iframe>' ;
28658
28659         return html ;
28660     },
28661     
28662     _insertHtmlBefore : function( html, element )
28663     {
28664         if ( element.insertAdjacentHTML )       {
28665             // IE
28666             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28667         } else { // Gecko
28668             var oRange = document.createRange() ;
28669             oRange.setStartBefore( element ) ;
28670             var oFragment = oRange.createContextualFragment( html );
28671             element.parentNode.insertBefore( oFragment, element ) ;
28672         }
28673     }
28674     
28675     
28676   
28677     
28678     
28679     
28680     
28681
28682 });
28683
28684 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28685
28686 function FCKeditor_OnComplete(editorInstance){
28687     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28688     f.fckEditor = editorInstance;
28689     //console.log("loaded");
28690     f.fireEvent('editorinit', f, editorInstance);
28691
28692   
28693
28694  
28695
28696
28697
28698
28699
28700
28701
28702
28703
28704
28705
28706
28707
28708
28709
28710 //<script type="text/javascript">
28711 /**
28712  * @class Roo.form.GridField
28713  * @extends Roo.form.Field
28714  * Embed a grid (or editable grid into a form)
28715  * STATUS ALPHA
28716  * 
28717  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28718  * it needs 
28719  * xgrid.store = Roo.data.Store
28720  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28721  * xgrid.store.reader = Roo.data.JsonReader 
28722  * 
28723  * 
28724  * @constructor
28725  * Creates a new GridField
28726  * @param {Object} config Configuration options
28727  */
28728 Roo.form.GridField = function(config){
28729     Roo.form.GridField.superclass.constructor.call(this, config);
28730      
28731 };
28732
28733 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28734     /**
28735      * @cfg {Number} width  - used to restrict width of grid..
28736      */
28737     width : 100,
28738     /**
28739      * @cfg {Number} height - used to restrict height of grid..
28740      */
28741     height : 50,
28742      /**
28743      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28744          * 
28745          *}
28746      */
28747     xgrid : false, 
28748     /**
28749      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28750      * {tag: "input", type: "checkbox", autocomplete: "off"})
28751      */
28752    // defaultAutoCreate : { tag: 'div' },
28753     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28754     /**
28755      * @cfg {String} addTitle Text to include for adding a title.
28756      */
28757     addTitle : false,
28758     //
28759     onResize : function(){
28760         Roo.form.Field.superclass.onResize.apply(this, arguments);
28761     },
28762
28763     initEvents : function(){
28764         // Roo.form.Checkbox.superclass.initEvents.call(this);
28765         // has no events...
28766        
28767     },
28768
28769
28770     getResizeEl : function(){
28771         return this.wrap;
28772     },
28773
28774     getPositionEl : function(){
28775         return this.wrap;
28776     },
28777
28778     // private
28779     onRender : function(ct, position){
28780         
28781         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28782         var style = this.style;
28783         delete this.style;
28784         
28785         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28786         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28787         this.viewEl = this.wrap.createChild({ tag: 'div' });
28788         if (style) {
28789             this.viewEl.applyStyles(style);
28790         }
28791         if (this.width) {
28792             this.viewEl.setWidth(this.width);
28793         }
28794         if (this.height) {
28795             this.viewEl.setHeight(this.height);
28796         }
28797         //if(this.inputValue !== undefined){
28798         //this.setValue(this.value);
28799         
28800         
28801         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28802         
28803         
28804         this.grid.render();
28805         this.grid.getDataSource().on('remove', this.refreshValue, this);
28806         this.grid.getDataSource().on('update', this.refreshValue, this);
28807         this.grid.on('afteredit', this.refreshValue, this);
28808  
28809     },
28810      
28811     
28812     /**
28813      * Sets the value of the item. 
28814      * @param {String} either an object  or a string..
28815      */
28816     setValue : function(v){
28817         //this.value = v;
28818         v = v || []; // empty set..
28819         // this does not seem smart - it really only affects memoryproxy grids..
28820         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28821             var ds = this.grid.getDataSource();
28822             // assumes a json reader..
28823             var data = {}
28824             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28825             ds.loadData( data);
28826         }
28827         // clear selection so it does not get stale.
28828         if (this.grid.sm) { 
28829             this.grid.sm.clearSelections();
28830         }
28831         
28832         Roo.form.GridField.superclass.setValue.call(this, v);
28833         this.refreshValue();
28834         // should load data in the grid really....
28835     },
28836     
28837     // private
28838     refreshValue: function() {
28839          var val = [];
28840         this.grid.getDataSource().each(function(r) {
28841             val.push(r.data);
28842         });
28843         this.el.dom.value = Roo.encode(val);
28844     }
28845     
28846      
28847     
28848     
28849 });/*
28850  * Based on:
28851  * Ext JS Library 1.1.1
28852  * Copyright(c) 2006-2007, Ext JS, LLC.
28853  *
28854  * Originally Released Under LGPL - original licence link has changed is not relivant.
28855  *
28856  * Fork - LGPL
28857  * <script type="text/javascript">
28858  */
28859 /**
28860  * @class Roo.form.DisplayField
28861  * @extends Roo.form.Field
28862  * A generic Field to display non-editable data.
28863  * @constructor
28864  * Creates a new Display Field item.
28865  * @param {Object} config Configuration options
28866  */
28867 Roo.form.DisplayField = function(config){
28868     Roo.form.DisplayField.superclass.constructor.call(this, config);
28869     
28870 };
28871
28872 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28873     inputType:      'hidden',
28874     allowBlank:     true,
28875     readOnly:         true,
28876     
28877  
28878     /**
28879      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28880      */
28881     focusClass : undefined,
28882     /**
28883      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28884      */
28885     fieldClass: 'x-form-field',
28886     
28887      /**
28888      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28889      */
28890     valueRenderer: undefined,
28891     
28892     width: 100,
28893     /**
28894      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28895      * {tag: "input", type: "checkbox", autocomplete: "off"})
28896      */
28897      
28898  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28899
28900     onResize : function(){
28901         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28902         
28903     },
28904
28905     initEvents : function(){
28906         // Roo.form.Checkbox.superclass.initEvents.call(this);
28907         // has no events...
28908        
28909     },
28910
28911
28912     getResizeEl : function(){
28913         return this.wrap;
28914     },
28915
28916     getPositionEl : function(){
28917         return this.wrap;
28918     },
28919
28920     // private
28921     onRender : function(ct, position){
28922         
28923         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28924         //if(this.inputValue !== undefined){
28925         this.wrap = this.el.wrap();
28926         
28927         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28928         
28929         if (this.bodyStyle) {
28930             this.viewEl.applyStyles(this.bodyStyle);
28931         }
28932         //this.viewEl.setStyle('padding', '2px');
28933         
28934         this.setValue(this.value);
28935         
28936     },
28937 /*
28938     // private
28939     initValue : Roo.emptyFn,
28940
28941   */
28942
28943         // private
28944     onClick : function(){
28945         
28946     },
28947
28948     /**
28949      * Sets the checked state of the checkbox.
28950      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28951      */
28952     setValue : function(v){
28953         this.value = v;
28954         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28955         // this might be called before we have a dom element..
28956         if (!this.viewEl) {
28957             return;
28958         }
28959         this.viewEl.dom.innerHTML = html;
28960         Roo.form.DisplayField.superclass.setValue.call(this, v);
28961
28962     }
28963 });/*
28964  * 
28965  * Licence- LGPL
28966  * 
28967  */
28968
28969 /**
28970  * @class Roo.form.DayPicker
28971  * @extends Roo.form.Field
28972  * A Day picker show [M] [T] [W] ....
28973  * @constructor
28974  * Creates a new Day Picker
28975  * @param {Object} config Configuration options
28976  */
28977 Roo.form.DayPicker= function(config){
28978     Roo.form.DayPicker.superclass.constructor.call(this, config);
28979      
28980 };
28981
28982 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28983     /**
28984      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28985      */
28986     focusClass : undefined,
28987     /**
28988      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28989      */
28990     fieldClass: "x-form-field",
28991    
28992     /**
28993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28994      * {tag: "input", type: "checkbox", autocomplete: "off"})
28995      */
28996     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28997     
28998    
28999     actionMode : 'viewEl', 
29000     //
29001     // private
29002  
29003     inputType : 'hidden',
29004     
29005      
29006     inputElement: false, // real input element?
29007     basedOn: false, // ????
29008     
29009     isFormField: true, // not sure where this is needed!!!!
29010
29011     onResize : function(){
29012         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29013         if(!this.boxLabel){
29014             this.el.alignTo(this.wrap, 'c-c');
29015         }
29016     },
29017
29018     initEvents : function(){
29019         Roo.form.Checkbox.superclass.initEvents.call(this);
29020         this.el.on("click", this.onClick,  this);
29021         this.el.on("change", this.onClick,  this);
29022     },
29023
29024
29025     getResizeEl : function(){
29026         return this.wrap;
29027     },
29028
29029     getPositionEl : function(){
29030         return this.wrap;
29031     },
29032
29033     
29034     // private
29035     onRender : function(ct, position){
29036         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29037        
29038         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29039         
29040         var r1 = '<table><tr>';
29041         var r2 = '<tr class="x-form-daypick-icons">';
29042         for (var i=0; i < 7; i++) {
29043             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29044             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29045         }
29046         
29047         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29048         viewEl.select('img').on('click', this.onClick, this);
29049         this.viewEl = viewEl;   
29050         
29051         
29052         // this will not work on Chrome!!!
29053         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29054         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29055         
29056         
29057           
29058
29059     },
29060
29061     // private
29062     initValue : Roo.emptyFn,
29063
29064     /**
29065      * Returns the checked state of the checkbox.
29066      * @return {Boolean} True if checked, else false
29067      */
29068     getValue : function(){
29069         return this.el.dom.value;
29070         
29071     },
29072
29073         // private
29074     onClick : function(e){ 
29075         //this.setChecked(!this.checked);
29076         Roo.get(e.target).toggleClass('x-menu-item-checked');
29077         this.refreshValue();
29078         //if(this.el.dom.checked != this.checked){
29079         //    this.setValue(this.el.dom.checked);
29080        // }
29081     },
29082     
29083     // private
29084     refreshValue : function()
29085     {
29086         var val = '';
29087         this.viewEl.select('img',true).each(function(e,i,n)  {
29088             val += e.is(".x-menu-item-checked") ? String(n) : '';
29089         });
29090         this.setValue(val, true);
29091     },
29092
29093     /**
29094      * Sets the checked state of the checkbox.
29095      * On is always based on a string comparison between inputValue and the param.
29096      * @param {Boolean/String} value - the value to set 
29097      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29098      */
29099     setValue : function(v,suppressEvent){
29100         if (!this.el.dom) {
29101             return;
29102         }
29103         var old = this.el.dom.value ;
29104         this.el.dom.value = v;
29105         if (suppressEvent) {
29106             return ;
29107         }
29108          
29109         // update display..
29110         this.viewEl.select('img',true).each(function(e,i,n)  {
29111             
29112             var on = e.is(".x-menu-item-checked");
29113             var newv = v.indexOf(String(n)) > -1;
29114             if (on != newv) {
29115                 e.toggleClass('x-menu-item-checked');
29116             }
29117             
29118         });
29119         
29120         
29121         this.fireEvent('change', this, v, old);
29122         
29123         
29124     },
29125    
29126     // handle setting of hidden value by some other method!!?!?
29127     setFromHidden: function()
29128     {
29129         if(!this.el){
29130             return;
29131         }
29132         //console.log("SET FROM HIDDEN");
29133         //alert('setFrom hidden');
29134         this.setValue(this.el.dom.value);
29135     },
29136     
29137     onDestroy : function()
29138     {
29139         if(this.viewEl){
29140             Roo.get(this.viewEl).remove();
29141         }
29142          
29143         Roo.form.DayPicker.superclass.onDestroy.call(this);
29144     }
29145
29146 });/*
29147  * RooJS Library 1.1.1
29148  * Copyright(c) 2008-2011  Alan Knowles
29149  *
29150  * License - LGPL
29151  */
29152  
29153
29154 /**
29155  * @class Roo.form.ComboCheck
29156  * @extends Roo.form.ComboBox
29157  * A combobox for multiple select items.
29158  *
29159  * FIXME - could do with a reset button..
29160  * 
29161  * @constructor
29162  * Create a new ComboCheck
29163  * @param {Object} config Configuration options
29164  */
29165 Roo.form.ComboCheck = function(config){
29166     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29167     // should verify some data...
29168     // like
29169     // hiddenName = required..
29170     // displayField = required
29171     // valudField == required
29172     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29173     var _t = this;
29174     Roo.each(req, function(e) {
29175         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29176             throw "Roo.form.ComboCheck : missing value for: " + e;
29177         }
29178     });
29179     
29180     
29181 };
29182
29183 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29184      
29185      
29186     editable : false,
29187      
29188     selectedClass: 'x-menu-item-checked', 
29189     
29190     // private
29191     onRender : function(ct, position){
29192         var _t = this;
29193         
29194         
29195         
29196         if(!this.tpl){
29197             var cls = 'x-combo-list';
29198
29199             
29200             this.tpl =  new Roo.Template({
29201                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29202                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29203                    '<span>{' + this.displayField + '}</span>' +
29204                     '</div>' 
29205                 
29206             });
29207         }
29208  
29209         
29210         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29211         this.view.singleSelect = false;
29212         this.view.multiSelect = true;
29213         this.view.toggleSelect = true;
29214         this.pageTb.add(new Roo.Toolbar.Fill(), {
29215             
29216             text: 'Done',
29217             handler: function()
29218             {
29219                 _t.collapse();
29220             }
29221         });
29222     },
29223     
29224     onViewOver : function(e, t){
29225         // do nothing...
29226         return;
29227         
29228     },
29229     
29230     onViewClick : function(doFocus,index){
29231         return;
29232         
29233     },
29234     select: function () {
29235         //Roo.log("SELECT CALLED");
29236     },
29237      
29238     selectByValue : function(xv, scrollIntoView){
29239         var ar = this.getValueArray();
29240         var sels = [];
29241         
29242         Roo.each(ar, function(v) {
29243             if(v === undefined || v === null){
29244                 return;
29245             }
29246             var r = this.findRecord(this.valueField, v);
29247             if(r){
29248                 sels.push(this.store.indexOf(r))
29249                 
29250             }
29251         },this);
29252         this.view.select(sels);
29253         return false;
29254     },
29255     
29256     
29257     
29258     onSelect : function(record, index){
29259        // Roo.log("onselect Called");
29260        // this is only called by the clear button now..
29261         this.view.clearSelections();
29262         this.setValue('[]');
29263         if (this.value != this.valueBefore) {
29264             this.fireEvent('change', this, this.value, this.valueBefore);
29265         }
29266     },
29267     getValueArray : function()
29268     {
29269         var ar = [] ;
29270         
29271         try {
29272             //Roo.log(this.value);
29273             if (typeof(this.value) == 'undefined') {
29274                 return [];
29275             }
29276             var ar = Roo.decode(this.value);
29277             return  ar instanceof Array ? ar : []; //?? valid?
29278             
29279         } catch(e) {
29280             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29281             return [];
29282         }
29283          
29284     },
29285     expand : function ()
29286     {
29287         Roo.form.ComboCheck.superclass.expand.call(this);
29288         this.valueBefore = this.value;
29289         
29290
29291     },
29292     
29293     collapse : function(){
29294         Roo.form.ComboCheck.superclass.collapse.call(this);
29295         var sl = this.view.getSelectedIndexes();
29296         var st = this.store;
29297         var nv = [];
29298         var tv = [];
29299         var r;
29300         Roo.each(sl, function(i) {
29301             r = st.getAt(i);
29302             nv.push(r.get(this.valueField));
29303         },this);
29304         this.setValue(Roo.encode(nv));
29305         if (this.value != this.valueBefore) {
29306
29307             this.fireEvent('change', this, this.value, this.valueBefore);
29308         }
29309         
29310     },
29311     
29312     setValue : function(v){
29313         // Roo.log(v);
29314         this.value = v;
29315         
29316         var vals = this.getValueArray();
29317         var tv = [];
29318         Roo.each(vals, function(k) {
29319             var r = this.findRecord(this.valueField, k);
29320             if(r){
29321                 tv.push(r.data[this.displayField]);
29322             }else if(this.valueNotFoundText !== undefined){
29323                 tv.push( this.valueNotFoundText );
29324             }
29325         },this);
29326        // Roo.log(tv);
29327         
29328         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29329         this.hiddenField.value = v;
29330         this.value = v;
29331     }
29332     
29333 });//<script type="text/javasscript">
29334  
29335
29336 /**
29337  * @class Roo.DDView
29338  * A DnD enabled version of Roo.View.
29339  * @param {Element/String} container The Element in which to create the View.
29340  * @param {String} tpl The template string used to create the markup for each element of the View
29341  * @param {Object} config The configuration properties. These include all the config options of
29342  * {@link Roo.View} plus some specific to this class.<br>
29343  * <p>
29344  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29345  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29346  * <p>
29347  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29348 .x-view-drag-insert-above {
29349         border-top:1px dotted #3366cc;
29350 }
29351 .x-view-drag-insert-below {
29352         border-bottom:1px dotted #3366cc;
29353 }
29354 </code></pre>
29355  * 
29356  */
29357  
29358 Roo.DDView = function(container, tpl, config) {
29359     Roo.DDView.superclass.constructor.apply(this, arguments);
29360     this.getEl().setStyle("outline", "0px none");
29361     this.getEl().unselectable();
29362     if (this.dragGroup) {
29363                 this.setDraggable(this.dragGroup.split(","));
29364     }
29365     if (this.dropGroup) {
29366                 this.setDroppable(this.dropGroup.split(","));
29367     }
29368     if (this.deletable) {
29369         this.setDeletable();
29370     }
29371     this.isDirtyFlag = false;
29372         this.addEvents({
29373                 "drop" : true
29374         });
29375 };
29376
29377 Roo.extend(Roo.DDView, Roo.View, {
29378 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
29379 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
29380 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
29381 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
29382
29383         isFormField: true,
29384
29385         reset: Roo.emptyFn,
29386         
29387         clearInvalid: Roo.form.Field.prototype.clearInvalid,
29388
29389         validate: function() {
29390                 return true;
29391         },
29392         
29393         destroy: function() {
29394                 this.purgeListeners();
29395                 this.getEl.removeAllListeners();
29396                 this.getEl().remove();
29397                 if (this.dragZone) {
29398                         if (this.dragZone.destroy) {
29399                                 this.dragZone.destroy();
29400                         }
29401                 }
29402                 if (this.dropZone) {
29403                         if (this.dropZone.destroy) {
29404                                 this.dropZone.destroy();
29405                         }
29406                 }
29407         },
29408
29409 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
29410         getName: function() {
29411                 return this.name;
29412         },
29413
29414 /**     Loads the View from a JSON string representing the Records to put into the Store. */
29415         setValue: function(v) {
29416                 if (!this.store) {
29417                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
29418                 }
29419                 var data = {};
29420                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
29421                 this.store.proxy = new Roo.data.MemoryProxy(data);
29422                 this.store.load();
29423         },
29424
29425 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
29426         getValue: function() {
29427                 var result = '(';
29428                 this.store.each(function(rec) {
29429                         result += rec.id + ',';
29430                 });
29431                 return result.substr(0, result.length - 1) + ')';
29432         },
29433         
29434         getIds: function() {
29435                 var i = 0, result = new Array(this.store.getCount());
29436                 this.store.each(function(rec) {
29437                         result[i++] = rec.id;
29438                 });
29439                 return result;
29440         },
29441         
29442         isDirty: function() {
29443                 return this.isDirtyFlag;
29444         },
29445
29446 /**
29447  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
29448  *      whole Element becomes the target, and this causes the drop gesture to append.
29449  */
29450     getTargetFromEvent : function(e) {
29451                 var target = e.getTarget();
29452                 while ((target !== null) && (target.parentNode != this.el.dom)) {
29453                 target = target.parentNode;
29454                 }
29455                 if (!target) {
29456                         target = this.el.dom.lastChild || this.el.dom;
29457                 }
29458                 return target;
29459     },
29460
29461 /**
29462  *      Create the drag data which consists of an object which has the property "ddel" as
29463  *      the drag proxy element. 
29464  */
29465     getDragData : function(e) {
29466         var target = this.findItemFromChild(e.getTarget());
29467                 if(target) {
29468                         this.handleSelection(e);
29469                         var selNodes = this.getSelectedNodes();
29470             var dragData = {
29471                 source: this,
29472                 copy: this.copy || (this.allowCopy && e.ctrlKey),
29473                 nodes: selNodes,
29474                 records: []
29475                         };
29476                         var selectedIndices = this.getSelectedIndexes();
29477                         for (var i = 0; i < selectedIndices.length; i++) {
29478                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
29479                         }
29480                         if (selNodes.length == 1) {
29481                                 dragData.ddel = target.cloneNode(true); // the div element
29482                         } else {
29483                                 var div = document.createElement('div'); // create the multi element drag "ghost"
29484                                 div.className = 'multi-proxy';
29485                                 for (var i = 0, len = selNodes.length; i < len; i++) {
29486                                         div.appendChild(selNodes[i].cloneNode(true));
29487                                 }
29488                                 dragData.ddel = div;
29489                         }
29490             //console.log(dragData)
29491             //console.log(dragData.ddel.innerHTML)
29492                         return dragData;
29493                 }
29494         //console.log('nodragData')
29495                 return false;
29496     },
29497     
29498 /**     Specify to which ddGroup items in this DDView may be dragged. */
29499     setDraggable: function(ddGroup) {
29500         if (ddGroup instanceof Array) {
29501                 Roo.each(ddGroup, this.setDraggable, this);
29502                 return;
29503         }
29504         if (this.dragZone) {
29505                 this.dragZone.addToGroup(ddGroup);
29506         } else {
29507                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
29508                                 containerScroll: true,
29509                                 ddGroup: ddGroup 
29510
29511                         });
29512 //                      Draggability implies selection. DragZone's mousedown selects the element.
29513                         if (!this.multiSelect) { this.singleSelect = true; }
29514
29515 //                      Wire the DragZone's handlers up to methods in *this*
29516                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
29517                 }
29518     },
29519
29520 /**     Specify from which ddGroup this DDView accepts drops. */
29521     setDroppable: function(ddGroup) {
29522         if (ddGroup instanceof Array) {
29523                 Roo.each(ddGroup, this.setDroppable, this);
29524                 return;
29525         }
29526         if (this.dropZone) {
29527                 this.dropZone.addToGroup(ddGroup);
29528         } else {
29529                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
29530                                 containerScroll: true,
29531                                 ddGroup: ddGroup
29532                         });
29533
29534 //                      Wire the DropZone's handlers up to methods in *this*
29535                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
29536                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
29537                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
29538                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
29539                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
29540                 }
29541     },
29542
29543 /**     Decide whether to drop above or below a View node. */
29544     getDropPoint : function(e, n, dd){
29545         if (n == this.el.dom) { return "above"; }
29546                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29547                 var c = t + (b - t) / 2;
29548                 var y = Roo.lib.Event.getPageY(e);
29549                 if(y <= c) {
29550                         return "above";
29551                 }else{
29552                         return "below";
29553                 }
29554     },
29555
29556     onNodeEnter : function(n, dd, e, data){
29557                 return false;
29558     },
29559     
29560     onNodeOver : function(n, dd, e, data){
29561                 var pt = this.getDropPoint(e, n, dd);
29562                 // set the insert point style on the target node
29563                 var dragElClass = this.dropNotAllowed;
29564                 if (pt) {
29565                         var targetElClass;
29566                         if (pt == "above"){
29567                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29568                                 targetElClass = "x-view-drag-insert-above";
29569                         } else {
29570                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29571                                 targetElClass = "x-view-drag-insert-below";
29572                         }
29573                         if (this.lastInsertClass != targetElClass){
29574                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29575                                 this.lastInsertClass = targetElClass;
29576                         }
29577                 }
29578                 return dragElClass;
29579         },
29580
29581     onNodeOut : function(n, dd, e, data){
29582                 this.removeDropIndicators(n);
29583     },
29584
29585     onNodeDrop : function(n, dd, e, data){
29586         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29587                 return false;
29588         }
29589         var pt = this.getDropPoint(e, n, dd);
29590                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29591                 if (pt == "below") { insertAt++; }
29592                 for (var i = 0; i < data.records.length; i++) {
29593                         var r = data.records[i];
29594                         var dup = this.store.getById(r.id);
29595                         if (dup && (dd != this.dragZone)) {
29596                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29597                         } else {
29598                                 if (data.copy) {
29599                                         this.store.insert(insertAt++, r.copy());
29600                                 } else {
29601                                         data.source.isDirtyFlag = true;
29602                                         r.store.remove(r);
29603                                         this.store.insert(insertAt++, r);
29604                                 }
29605                                 this.isDirtyFlag = true;
29606                         }
29607                 }
29608                 this.dragZone.cachedTarget = null;
29609                 return true;
29610     },
29611
29612     removeDropIndicators : function(n){
29613                 if(n){
29614                         Roo.fly(n).removeClass([
29615                                 "x-view-drag-insert-above",
29616                                 "x-view-drag-insert-below"]);
29617                         this.lastInsertClass = "_noclass";
29618                 }
29619     },
29620
29621 /**
29622  *      Utility method. Add a delete option to the DDView's context menu.
29623  *      @param {String} imageUrl The URL of the "delete" icon image.
29624  */
29625         setDeletable: function(imageUrl) {
29626                 if (!this.singleSelect && !this.multiSelect) {
29627                         this.singleSelect = true;
29628                 }
29629                 var c = this.getContextMenu();
29630                 this.contextMenu.on("itemclick", function(item) {
29631                         switch (item.id) {
29632                                 case "delete":
29633                                         this.remove(this.getSelectedIndexes());
29634                                         break;
29635                         }
29636                 }, this);
29637                 this.contextMenu.add({
29638                         icon: imageUrl,
29639                         id: "delete",
29640                         text: 'Delete'
29641                 });
29642         },
29643         
29644 /**     Return the context menu for this DDView. */
29645         getContextMenu: function() {
29646                 if (!this.contextMenu) {
29647 //                      Create the View's context menu
29648                         this.contextMenu = new Roo.menu.Menu({
29649                                 id: this.id + "-contextmenu"
29650                         });
29651                         this.el.on("contextmenu", this.showContextMenu, this);
29652                 }
29653                 return this.contextMenu;
29654         },
29655         
29656         disableContextMenu: function() {
29657                 if (this.contextMenu) {
29658                         this.el.un("contextmenu", this.showContextMenu, this);
29659                 }
29660         },
29661
29662         showContextMenu: function(e, item) {
29663         item = this.findItemFromChild(e.getTarget());
29664                 if (item) {
29665                         e.stopEvent();
29666                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29667                         this.contextMenu.showAt(e.getXY());
29668             }
29669     },
29670
29671 /**
29672  *      Remove {@link Roo.data.Record}s at the specified indices.
29673  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29674  */
29675     remove: function(selectedIndices) {
29676                 selectedIndices = [].concat(selectedIndices);
29677                 for (var i = 0; i < selectedIndices.length; i++) {
29678                         var rec = this.store.getAt(selectedIndices[i]);
29679                         this.store.remove(rec);
29680                 }
29681     },
29682
29683 /**
29684  *      Double click fires the event, but also, if this is draggable, and there is only one other
29685  *      related DropZone, it transfers the selected node.
29686  */
29687     onDblClick : function(e){
29688         var item = this.findItemFromChild(e.getTarget());
29689         if(item){
29690             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29691                 return false;
29692             }
29693             if (this.dragGroup) {
29694                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29695                     while (targets.indexOf(this.dropZone) > -1) {
29696                             targets.remove(this.dropZone);
29697                                 }
29698                     if (targets.length == 1) {
29699                                         this.dragZone.cachedTarget = null;
29700                         var el = Roo.get(targets[0].getEl());
29701                         var box = el.getBox(true);
29702                         targets[0].onNodeDrop(el.dom, {
29703                                 target: el.dom,
29704                                 xy: [box.x, box.y + box.height - 1]
29705                         }, null, this.getDragData(e));
29706                     }
29707                 }
29708         }
29709     },
29710     
29711     handleSelection: function(e) {
29712                 this.dragZone.cachedTarget = null;
29713         var item = this.findItemFromChild(e.getTarget());
29714         if (!item) {
29715                 this.clearSelections(true);
29716                 return;
29717         }
29718                 if (item && (this.multiSelect || this.singleSelect)){
29719                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29720                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29721                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29722                                 this.unselect(item);
29723                         } else {
29724                                 this.select(item, this.multiSelect && e.ctrlKey);
29725                                 this.lastSelection = item;
29726                         }
29727                 }
29728     },
29729
29730     onItemClick : function(item, index, e){
29731                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29732                         return false;
29733                 }
29734                 return true;
29735     },
29736
29737     unselect : function(nodeInfo, suppressEvent){
29738                 var node = this.getNode(nodeInfo);
29739                 if(node && this.isSelected(node)){
29740                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29741                                 Roo.fly(node).removeClass(this.selectedClass);
29742                                 this.selections.remove(node);
29743                                 if(!suppressEvent){
29744                                         this.fireEvent("selectionchange", this, this.selections);
29745                                 }
29746                         }
29747                 }
29748     }
29749 });
29750 /*
29751  * Based on:
29752  * Ext JS Library 1.1.1
29753  * Copyright(c) 2006-2007, Ext JS, LLC.
29754  *
29755  * Originally Released Under LGPL - original licence link has changed is not relivant.
29756  *
29757  * Fork - LGPL
29758  * <script type="text/javascript">
29759  */
29760  
29761 /**
29762  * @class Roo.LayoutManager
29763  * @extends Roo.util.Observable
29764  * Base class for layout managers.
29765  */
29766 Roo.LayoutManager = function(container, config){
29767     Roo.LayoutManager.superclass.constructor.call(this);
29768     this.el = Roo.get(container);
29769     // ie scrollbar fix
29770     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29771         document.body.scroll = "no";
29772     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29773         this.el.position('relative');
29774     }
29775     this.id = this.el.id;
29776     this.el.addClass("x-layout-container");
29777     /** false to disable window resize monitoring @type Boolean */
29778     this.monitorWindowResize = true;
29779     this.regions = {};
29780     this.addEvents({
29781         /**
29782          * @event layout
29783          * Fires when a layout is performed. 
29784          * @param {Roo.LayoutManager} this
29785          */
29786         "layout" : true,
29787         /**
29788          * @event regionresized
29789          * Fires when the user resizes a region. 
29790          * @param {Roo.LayoutRegion} region The resized region
29791          * @param {Number} newSize The new size (width for east/west, height for north/south)
29792          */
29793         "regionresized" : true,
29794         /**
29795          * @event regioncollapsed
29796          * Fires when a region is collapsed. 
29797          * @param {Roo.LayoutRegion} region The collapsed region
29798          */
29799         "regioncollapsed" : true,
29800         /**
29801          * @event regionexpanded
29802          * Fires when a region is expanded.  
29803          * @param {Roo.LayoutRegion} region The expanded region
29804          */
29805         "regionexpanded" : true
29806     });
29807     this.updating = false;
29808     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29809 };
29810
29811 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29812     /**
29813      * Returns true if this layout is currently being updated
29814      * @return {Boolean}
29815      */
29816     isUpdating : function(){
29817         return this.updating; 
29818     },
29819     
29820     /**
29821      * Suspend the LayoutManager from doing auto-layouts while
29822      * making multiple add or remove calls
29823      */
29824     beginUpdate : function(){
29825         this.updating = true;    
29826     },
29827     
29828     /**
29829      * Restore auto-layouts and optionally disable the manager from performing a layout
29830      * @param {Boolean} noLayout true to disable a layout update 
29831      */
29832     endUpdate : function(noLayout){
29833         this.updating = false;
29834         if(!noLayout){
29835             this.layout();
29836         }    
29837     },
29838     
29839     layout: function(){
29840         
29841     },
29842     
29843     onRegionResized : function(region, newSize){
29844         this.fireEvent("regionresized", region, newSize);
29845         this.layout();
29846     },
29847     
29848     onRegionCollapsed : function(region){
29849         this.fireEvent("regioncollapsed", region);
29850     },
29851     
29852     onRegionExpanded : function(region){
29853         this.fireEvent("regionexpanded", region);
29854     },
29855         
29856     /**
29857      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29858      * performs box-model adjustments.
29859      * @return {Object} The size as an object {width: (the width), height: (the height)}
29860      */
29861     getViewSize : function(){
29862         var size;
29863         if(this.el.dom != document.body){
29864             size = this.el.getSize();
29865         }else{
29866             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29867         }
29868         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29869         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29870         return size;
29871     },
29872     
29873     /**
29874      * Returns the Element this layout is bound to.
29875      * @return {Roo.Element}
29876      */
29877     getEl : function(){
29878         return this.el;
29879     },
29880     
29881     /**
29882      * Returns the specified region.
29883      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29884      * @return {Roo.LayoutRegion}
29885      */
29886     getRegion : function(target){
29887         return this.regions[target.toLowerCase()];
29888     },
29889     
29890     onWindowResize : function(){
29891         if(this.monitorWindowResize){
29892             this.layout();
29893         }
29894     }
29895 });/*
29896  * Based on:
29897  * Ext JS Library 1.1.1
29898  * Copyright(c) 2006-2007, Ext JS, LLC.
29899  *
29900  * Originally Released Under LGPL - original licence link has changed is not relivant.
29901  *
29902  * Fork - LGPL
29903  * <script type="text/javascript">
29904  */
29905 /**
29906  * @class Roo.BorderLayout
29907  * @extends Roo.LayoutManager
29908  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29909  * please see: <br><br>
29910  * <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>
29911  * <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>
29912  * Example:
29913  <pre><code>
29914  var layout = new Roo.BorderLayout(document.body, {
29915     north: {
29916         initialSize: 25,
29917         titlebar: false
29918     },
29919     west: {
29920         split:true,
29921         initialSize: 200,
29922         minSize: 175,
29923         maxSize: 400,
29924         titlebar: true,
29925         collapsible: true
29926     },
29927     east: {
29928         split:true,
29929         initialSize: 202,
29930         minSize: 175,
29931         maxSize: 400,
29932         titlebar: true,
29933         collapsible: true
29934     },
29935     south: {
29936         split:true,
29937         initialSize: 100,
29938         minSize: 100,
29939         maxSize: 200,
29940         titlebar: true,
29941         collapsible: true
29942     },
29943     center: {
29944         titlebar: true,
29945         autoScroll:true,
29946         resizeTabs: true,
29947         minTabWidth: 50,
29948         preferredTabWidth: 150
29949     }
29950 });
29951
29952 // shorthand
29953 var CP = Roo.ContentPanel;
29954
29955 layout.beginUpdate();
29956 layout.add("north", new CP("north", "North"));
29957 layout.add("south", new CP("south", {title: "South", closable: true}));
29958 layout.add("west", new CP("west", {title: "West"}));
29959 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29960 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29961 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29962 layout.getRegion("center").showPanel("center1");
29963 layout.endUpdate();
29964 </code></pre>
29965
29966 <b>The container the layout is rendered into can be either the body element or any other element.
29967 If it is not the body element, the container needs to either be an absolute positioned element,
29968 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29969 the container size if it is not the body element.</b>
29970
29971 * @constructor
29972 * Create a new BorderLayout
29973 * @param {String/HTMLElement/Element} container The container this layout is bound to
29974 * @param {Object} config Configuration options
29975  */
29976 Roo.BorderLayout = function(container, config){
29977     config = config || {};
29978     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29979     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29980     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29981         var target = this.factory.validRegions[i];
29982         if(config[target]){
29983             this.addRegion(target, config[target]);
29984         }
29985     }
29986 };
29987
29988 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29989     /**
29990      * Creates and adds a new region if it doesn't already exist.
29991      * @param {String} target The target region key (north, south, east, west or center).
29992      * @param {Object} config The regions config object
29993      * @return {BorderLayoutRegion} The new region
29994      */
29995     addRegion : function(target, config){
29996         if(!this.regions[target]){
29997             var r = this.factory.create(target, this, config);
29998             this.bindRegion(target, r);
29999         }
30000         return this.regions[target];
30001     },
30002
30003     // private (kinda)
30004     bindRegion : function(name, r){
30005         this.regions[name] = r;
30006         r.on("visibilitychange", this.layout, this);
30007         r.on("paneladded", this.layout, this);
30008         r.on("panelremoved", this.layout, this);
30009         r.on("invalidated", this.layout, this);
30010         r.on("resized", this.onRegionResized, this);
30011         r.on("collapsed", this.onRegionCollapsed, this);
30012         r.on("expanded", this.onRegionExpanded, this);
30013     },
30014
30015     /**
30016      * Performs a layout update.
30017      */
30018     layout : function(){
30019         if(this.updating) return;
30020         var size = this.getViewSize();
30021         var w = size.width;
30022         var h = size.height;
30023         var centerW = w;
30024         var centerH = h;
30025         var centerY = 0;
30026         var centerX = 0;
30027         //var x = 0, y = 0;
30028
30029         var rs = this.regions;
30030         var north = rs["north"];
30031         var south = rs["south"]; 
30032         var west = rs["west"];
30033         var east = rs["east"];
30034         var center = rs["center"];
30035         //if(this.hideOnLayout){ // not supported anymore
30036             //c.el.setStyle("display", "none");
30037         //}
30038         if(north && north.isVisible()){
30039             var b = north.getBox();
30040             var m = north.getMargins();
30041             b.width = w - (m.left+m.right);
30042             b.x = m.left;
30043             b.y = m.top;
30044             centerY = b.height + b.y + m.bottom;
30045             centerH -= centerY;
30046             north.updateBox(this.safeBox(b));
30047         }
30048         if(south && south.isVisible()){
30049             var b = south.getBox();
30050             var m = south.getMargins();
30051             b.width = w - (m.left+m.right);
30052             b.x = m.left;
30053             var totalHeight = (b.height + m.top + m.bottom);
30054             b.y = h - totalHeight + m.top;
30055             centerH -= totalHeight;
30056             south.updateBox(this.safeBox(b));
30057         }
30058         if(west && west.isVisible()){
30059             var b = west.getBox();
30060             var m = west.getMargins();
30061             b.height = centerH - (m.top+m.bottom);
30062             b.x = m.left;
30063             b.y = centerY + m.top;
30064             var totalWidth = (b.width + m.left + m.right);
30065             centerX += totalWidth;
30066             centerW -= totalWidth;
30067             west.updateBox(this.safeBox(b));
30068         }
30069         if(east && east.isVisible()){
30070             var b = east.getBox();
30071             var m = east.getMargins();
30072             b.height = centerH - (m.top+m.bottom);
30073             var totalWidth = (b.width + m.left + m.right);
30074             b.x = w - totalWidth + m.left;
30075             b.y = centerY + m.top;
30076             centerW -= totalWidth;
30077             east.updateBox(this.safeBox(b));
30078         }
30079         if(center){
30080             var m = center.getMargins();
30081             var centerBox = {
30082                 x: centerX + m.left,
30083                 y: centerY + m.top,
30084                 width: centerW - (m.left+m.right),
30085                 height: centerH - (m.top+m.bottom)
30086             };
30087             //if(this.hideOnLayout){
30088                 //center.el.setStyle("display", "block");
30089             //}
30090             center.updateBox(this.safeBox(centerBox));
30091         }
30092         this.el.repaint();
30093         this.fireEvent("layout", this);
30094     },
30095
30096     // private
30097     safeBox : function(box){
30098         box.width = Math.max(0, box.width);
30099         box.height = Math.max(0, box.height);
30100         return box;
30101     },
30102
30103     /**
30104      * Adds a ContentPanel (or subclass) to this layout.
30105      * @param {String} target The target region key (north, south, east, west or center).
30106      * @param {Roo.ContentPanel} panel The panel to add
30107      * @return {Roo.ContentPanel} The added panel
30108      */
30109     add : function(target, panel){
30110          
30111         target = target.toLowerCase();
30112         return this.regions[target].add(panel);
30113     },
30114
30115     /**
30116      * Remove a ContentPanel (or subclass) to this layout.
30117      * @param {String} target The target region key (north, south, east, west or center).
30118      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30119      * @return {Roo.ContentPanel} The removed panel
30120      */
30121     remove : function(target, panel){
30122         target = target.toLowerCase();
30123         return this.regions[target].remove(panel);
30124     },
30125
30126     /**
30127      * Searches all regions for a panel with the specified id
30128      * @param {String} panelId
30129      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30130      */
30131     findPanel : function(panelId){
30132         var rs = this.regions;
30133         for(var target in rs){
30134             if(typeof rs[target] != "function"){
30135                 var p = rs[target].getPanel(panelId);
30136                 if(p){
30137                     return p;
30138                 }
30139             }
30140         }
30141         return null;
30142     },
30143
30144     /**
30145      * Searches all regions for a panel with the specified id and activates (shows) it.
30146      * @param {String/ContentPanel} panelId The panels id or the panel itself
30147      * @return {Roo.ContentPanel} The shown panel or null
30148      */
30149     showPanel : function(panelId) {
30150       var rs = this.regions;
30151       for(var target in rs){
30152          var r = rs[target];
30153          if(typeof r != "function"){
30154             if(r.hasPanel(panelId)){
30155                return r.showPanel(panelId);
30156             }
30157          }
30158       }
30159       return null;
30160    },
30161
30162    /**
30163      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30164      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30165      */
30166     restoreState : function(provider){
30167         if(!provider){
30168             provider = Roo.state.Manager;
30169         }
30170         var sm = new Roo.LayoutStateManager();
30171         sm.init(this, provider);
30172     },
30173
30174     /**
30175      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30176      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30177      * a valid ContentPanel config object.  Example:
30178      * <pre><code>
30179 // Create the main layout
30180 var layout = new Roo.BorderLayout('main-ct', {
30181     west: {
30182         split:true,
30183         minSize: 175,
30184         titlebar: true
30185     },
30186     center: {
30187         title:'Components'
30188     }
30189 }, 'main-ct');
30190
30191 // Create and add multiple ContentPanels at once via configs
30192 layout.batchAdd({
30193    west: {
30194        id: 'source-files',
30195        autoCreate:true,
30196        title:'Ext Source Files',
30197        autoScroll:true,
30198        fitToFrame:true
30199    },
30200    center : {
30201        el: cview,
30202        autoScroll:true,
30203        fitToFrame:true,
30204        toolbar: tb,
30205        resizeEl:'cbody'
30206    }
30207 });
30208 </code></pre>
30209      * @param {Object} regions An object containing ContentPanel configs by region name
30210      */
30211     batchAdd : function(regions){
30212         this.beginUpdate();
30213         for(var rname in regions){
30214             var lr = this.regions[rname];
30215             if(lr){
30216                 this.addTypedPanels(lr, regions[rname]);
30217             }
30218         }
30219         this.endUpdate();
30220     },
30221
30222     // private
30223     addTypedPanels : function(lr, ps){
30224         if(typeof ps == 'string'){
30225             lr.add(new Roo.ContentPanel(ps));
30226         }
30227         else if(ps instanceof Array){
30228             for(var i =0, len = ps.length; i < len; i++){
30229                 this.addTypedPanels(lr, ps[i]);
30230             }
30231         }
30232         else if(!ps.events){ // raw config?
30233             var el = ps.el;
30234             delete ps.el; // prevent conflict
30235             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30236         }
30237         else {  // panel object assumed!
30238             lr.add(ps);
30239         }
30240     },
30241     /**
30242      * Adds a xtype elements to the layout.
30243      * <pre><code>
30244
30245 layout.addxtype({
30246        xtype : 'ContentPanel',
30247        region: 'west',
30248        items: [ .... ]
30249    }
30250 );
30251
30252 layout.addxtype({
30253         xtype : 'NestedLayoutPanel',
30254         region: 'west',
30255         layout: {
30256            center: { },
30257            west: { }   
30258         },
30259         items : [ ... list of content panels or nested layout panels.. ]
30260    }
30261 );
30262 </code></pre>
30263      * @param {Object} cfg Xtype definition of item to add.
30264      */
30265     addxtype : function(cfg)
30266     {
30267         // basically accepts a pannel...
30268         // can accept a layout region..!?!?
30269         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30270         
30271         if (!cfg.xtype.match(/Panel$/)) {
30272             return false;
30273         }
30274         var ret = false;
30275         
30276         if (typeof(cfg.region) == 'undefined') {
30277             Roo.log("Failed to add Panel, region was not set");
30278             Roo.log(cfg);
30279             return false;
30280         }
30281         var region = cfg.region;
30282         delete cfg.region;
30283         
30284           
30285         var xitems = [];
30286         if (cfg.items) {
30287             xitems = cfg.items;
30288             delete cfg.items;
30289         }
30290         var nb = false;
30291         
30292         switch(cfg.xtype) 
30293         {
30294             case 'ContentPanel':  // ContentPanel (el, cfg)
30295             case 'ScrollPanel':  // ContentPanel (el, cfg)
30296                 if(cfg.autoCreate) {
30297                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30298                 } else {
30299                     var el = this.el.createChild();
30300                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30301                 }
30302                 
30303                 this.add(region, ret);
30304                 break;
30305             
30306             
30307             case 'TreePanel': // our new panel!
30308                 cfg.el = this.el.createChild();
30309                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30310                 this.add(region, ret);
30311                 break;
30312             
30313             case 'NestedLayoutPanel': 
30314                 // create a new Layout (which is  a Border Layout...
30315                 var el = this.el.createChild();
30316                 var clayout = cfg.layout;
30317                 delete cfg.layout;
30318                 clayout.items   = clayout.items  || [];
30319                 // replace this exitems with the clayout ones..
30320                 xitems = clayout.items;
30321                  
30322                 
30323                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30324                     cfg.background = false;
30325                 }
30326                 var layout = new Roo.BorderLayout(el, clayout);
30327                 
30328                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30329                 //console.log('adding nested layout panel '  + cfg.toSource());
30330                 this.add(region, ret);
30331                 nb = {}; /// find first...
30332                 break;
30333                 
30334             case 'GridPanel': 
30335             
30336                 // needs grid and region
30337                 
30338                 //var el = this.getRegion(region).el.createChild();
30339                 var el = this.el.createChild();
30340                 // create the grid first...
30341                 
30342                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30343                 delete cfg.grid;
30344                 if (region == 'center' && this.active ) {
30345                     cfg.background = false;
30346                 }
30347                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30348                 
30349                 this.add(region, ret);
30350                 if (cfg.background) {
30351                     ret.on('activate', function(gp) {
30352                         if (!gp.grid.rendered) {
30353                             gp.grid.render();
30354                         }
30355                     });
30356                 } else {
30357                     grid.render();
30358                 }
30359                 break;
30360            
30361                
30362                 
30363                 
30364             default: 
30365                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30366                 return null;
30367              // GridPanel (grid, cfg)
30368             
30369         }
30370         this.beginUpdate();
30371         // add children..
30372         var region = '';
30373         var abn = {};
30374         Roo.each(xitems, function(i)  {
30375             region = nb && i.region ? i.region : false;
30376             
30377             var add = ret.addxtype(i);
30378            
30379             if (region) {
30380                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
30381                 if (!i.background) {
30382                     abn[region] = nb[region] ;
30383                 }
30384             }
30385             
30386         });
30387         this.endUpdate();
30388
30389         // make the last non-background panel active..
30390         //if (nb) { Roo.log(abn); }
30391         if (nb) {
30392             
30393             for(var r in abn) {
30394                 region = this.getRegion(r);
30395                 if (region) {
30396                     // tried using nb[r], but it does not work..
30397                      
30398                     region.showPanel(abn[r]);
30399                    
30400                 }
30401             }
30402         }
30403         return ret;
30404         
30405     }
30406 });
30407
30408 /**
30409  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
30410  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
30411  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
30412  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
30413  * <pre><code>
30414 // shorthand
30415 var CP = Roo.ContentPanel;
30416
30417 var layout = Roo.BorderLayout.create({
30418     north: {
30419         initialSize: 25,
30420         titlebar: false,
30421         panels: [new CP("north", "North")]
30422     },
30423     west: {
30424         split:true,
30425         initialSize: 200,
30426         minSize: 175,
30427         maxSize: 400,
30428         titlebar: true,
30429         collapsible: true,
30430         panels: [new CP("west", {title: "West"})]
30431     },
30432     east: {
30433         split:true,
30434         initialSize: 202,
30435         minSize: 175,
30436         maxSize: 400,
30437         titlebar: true,
30438         collapsible: true,
30439         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
30440     },
30441     south: {
30442         split:true,
30443         initialSize: 100,
30444         minSize: 100,
30445         maxSize: 200,
30446         titlebar: true,
30447         collapsible: true,
30448         panels: [new CP("south", {title: "South", closable: true})]
30449     },
30450     center: {
30451         titlebar: true,
30452         autoScroll:true,
30453         resizeTabs: true,
30454         minTabWidth: 50,
30455         preferredTabWidth: 150,
30456         panels: [
30457             new CP("center1", {title: "Close Me", closable: true}),
30458             new CP("center2", {title: "Center Panel", closable: false})
30459         ]
30460     }
30461 }, document.body);
30462
30463 layout.getRegion("center").showPanel("center1");
30464 </code></pre>
30465  * @param config
30466  * @param targetEl
30467  */
30468 Roo.BorderLayout.create = function(config, targetEl){
30469     var layout = new Roo.BorderLayout(targetEl || document.body, config);
30470     layout.beginUpdate();
30471     var regions = Roo.BorderLayout.RegionFactory.validRegions;
30472     for(var j = 0, jlen = regions.length; j < jlen; j++){
30473         var lr = regions[j];
30474         if(layout.regions[lr] && config[lr].panels){
30475             var r = layout.regions[lr];
30476             var ps = config[lr].panels;
30477             layout.addTypedPanels(r, ps);
30478         }
30479     }
30480     layout.endUpdate();
30481     return layout;
30482 };
30483
30484 // private
30485 Roo.BorderLayout.RegionFactory = {
30486     // private
30487     validRegions : ["north","south","east","west","center"],
30488
30489     // private
30490     create : function(target, mgr, config){
30491         target = target.toLowerCase();
30492         if(config.lightweight || config.basic){
30493             return new Roo.BasicLayoutRegion(mgr, config, target);
30494         }
30495         switch(target){
30496             case "north":
30497                 return new Roo.NorthLayoutRegion(mgr, config);
30498             case "south":
30499                 return new Roo.SouthLayoutRegion(mgr, config);
30500             case "east":
30501                 return new Roo.EastLayoutRegion(mgr, config);
30502             case "west":
30503                 return new Roo.WestLayoutRegion(mgr, config);
30504             case "center":
30505                 return new Roo.CenterLayoutRegion(mgr, config);
30506         }
30507         throw 'Layout region "'+target+'" not supported.';
30508     }
30509 };/*
30510  * Based on:
30511  * Ext JS Library 1.1.1
30512  * Copyright(c) 2006-2007, Ext JS, LLC.
30513  *
30514  * Originally Released Under LGPL - original licence link has changed is not relivant.
30515  *
30516  * Fork - LGPL
30517  * <script type="text/javascript">
30518  */
30519  
30520 /**
30521  * @class Roo.BasicLayoutRegion
30522  * @extends Roo.util.Observable
30523  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
30524  * and does not have a titlebar, tabs or any other features. All it does is size and position 
30525  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
30526  */
30527 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
30528     this.mgr = mgr;
30529     this.position  = pos;
30530     this.events = {
30531         /**
30532          * @scope Roo.BasicLayoutRegion
30533          */
30534         
30535         /**
30536          * @event beforeremove
30537          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30538          * @param {Roo.LayoutRegion} this
30539          * @param {Roo.ContentPanel} panel The panel
30540          * @param {Object} e The cancel event object
30541          */
30542         "beforeremove" : true,
30543         /**
30544          * @event invalidated
30545          * Fires when the layout for this region is changed.
30546          * @param {Roo.LayoutRegion} this
30547          */
30548         "invalidated" : true,
30549         /**
30550          * @event visibilitychange
30551          * Fires when this region is shown or hidden 
30552          * @param {Roo.LayoutRegion} this
30553          * @param {Boolean} visibility true or false
30554          */
30555         "visibilitychange" : true,
30556         /**
30557          * @event paneladded
30558          * Fires when a panel is added. 
30559          * @param {Roo.LayoutRegion} this
30560          * @param {Roo.ContentPanel} panel The panel
30561          */
30562         "paneladded" : true,
30563         /**
30564          * @event panelremoved
30565          * Fires when a panel is removed. 
30566          * @param {Roo.LayoutRegion} this
30567          * @param {Roo.ContentPanel} panel The panel
30568          */
30569         "panelremoved" : true,
30570         /**
30571          * @event collapsed
30572          * Fires when this region is collapsed.
30573          * @param {Roo.LayoutRegion} this
30574          */
30575         "collapsed" : true,
30576         /**
30577          * @event expanded
30578          * Fires when this region is expanded.
30579          * @param {Roo.LayoutRegion} this
30580          */
30581         "expanded" : true,
30582         /**
30583          * @event slideshow
30584          * Fires when this region is slid into view.
30585          * @param {Roo.LayoutRegion} this
30586          */
30587         "slideshow" : true,
30588         /**
30589          * @event slidehide
30590          * Fires when this region slides out of view. 
30591          * @param {Roo.LayoutRegion} this
30592          */
30593         "slidehide" : true,
30594         /**
30595          * @event panelactivated
30596          * Fires when a panel is activated. 
30597          * @param {Roo.LayoutRegion} this
30598          * @param {Roo.ContentPanel} panel The activated panel
30599          */
30600         "panelactivated" : true,
30601         /**
30602          * @event resized
30603          * Fires when the user resizes this region. 
30604          * @param {Roo.LayoutRegion} this
30605          * @param {Number} newSize The new size (width for east/west, height for north/south)
30606          */
30607         "resized" : true
30608     };
30609     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30610     this.panels = new Roo.util.MixedCollection();
30611     this.panels.getKey = this.getPanelId.createDelegate(this);
30612     this.box = null;
30613     this.activePanel = null;
30614     // ensure listeners are added...
30615     
30616     if (config.listeners || config.events) {
30617         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30618             listeners : config.listeners || {},
30619             events : config.events || {}
30620         });
30621     }
30622     
30623     if(skipConfig !== true){
30624         this.applyConfig(config);
30625     }
30626 };
30627
30628 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30629     getPanelId : function(p){
30630         return p.getId();
30631     },
30632     
30633     applyConfig : function(config){
30634         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30635         this.config = config;
30636         
30637     },
30638     
30639     /**
30640      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30641      * the width, for horizontal (north, south) the height.
30642      * @param {Number} newSize The new width or height
30643      */
30644     resizeTo : function(newSize){
30645         var el = this.el ? this.el :
30646                  (this.activePanel ? this.activePanel.getEl() : null);
30647         if(el){
30648             switch(this.position){
30649                 case "east":
30650                 case "west":
30651                     el.setWidth(newSize);
30652                     this.fireEvent("resized", this, newSize);
30653                 break;
30654                 case "north":
30655                 case "south":
30656                     el.setHeight(newSize);
30657                     this.fireEvent("resized", this, newSize);
30658                 break;                
30659             }
30660         }
30661     },
30662     
30663     getBox : function(){
30664         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30665     },
30666     
30667     getMargins : function(){
30668         return this.margins;
30669     },
30670     
30671     updateBox : function(box){
30672         this.box = box;
30673         var el = this.activePanel.getEl();
30674         el.dom.style.left = box.x + "px";
30675         el.dom.style.top = box.y + "px";
30676         this.activePanel.setSize(box.width, box.height);
30677     },
30678     
30679     /**
30680      * Returns the container element for this region.
30681      * @return {Roo.Element}
30682      */
30683     getEl : function(){
30684         return this.activePanel;
30685     },
30686     
30687     /**
30688      * Returns true if this region is currently visible.
30689      * @return {Boolean}
30690      */
30691     isVisible : function(){
30692         return this.activePanel ? true : false;
30693     },
30694     
30695     setActivePanel : function(panel){
30696         panel = this.getPanel(panel);
30697         if(this.activePanel && this.activePanel != panel){
30698             this.activePanel.setActiveState(false);
30699             this.activePanel.getEl().setLeftTop(-10000,-10000);
30700         }
30701         this.activePanel = panel;
30702         panel.setActiveState(true);
30703         if(this.box){
30704             panel.setSize(this.box.width, this.box.height);
30705         }
30706         this.fireEvent("panelactivated", this, panel);
30707         this.fireEvent("invalidated");
30708     },
30709     
30710     /**
30711      * Show the specified panel.
30712      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30713      * @return {Roo.ContentPanel} The shown panel or null
30714      */
30715     showPanel : function(panel){
30716         if(panel = this.getPanel(panel)){
30717             this.setActivePanel(panel);
30718         }
30719         return panel;
30720     },
30721     
30722     /**
30723      * Get the active panel for this region.
30724      * @return {Roo.ContentPanel} The active panel or null
30725      */
30726     getActivePanel : function(){
30727         return this.activePanel;
30728     },
30729     
30730     /**
30731      * Add the passed ContentPanel(s)
30732      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30733      * @return {Roo.ContentPanel} The panel added (if only one was added)
30734      */
30735     add : function(panel){
30736         if(arguments.length > 1){
30737             for(var i = 0, len = arguments.length; i < len; i++) {
30738                 this.add(arguments[i]);
30739             }
30740             return null;
30741         }
30742         if(this.hasPanel(panel)){
30743             this.showPanel(panel);
30744             return panel;
30745         }
30746         var el = panel.getEl();
30747         if(el.dom.parentNode != this.mgr.el.dom){
30748             this.mgr.el.dom.appendChild(el.dom);
30749         }
30750         if(panel.setRegion){
30751             panel.setRegion(this);
30752         }
30753         this.panels.add(panel);
30754         el.setStyle("position", "absolute");
30755         if(!panel.background){
30756             this.setActivePanel(panel);
30757             if(this.config.initialSize && this.panels.getCount()==1){
30758                 this.resizeTo(this.config.initialSize);
30759             }
30760         }
30761         this.fireEvent("paneladded", this, panel);
30762         return panel;
30763     },
30764     
30765     /**
30766      * Returns true if the panel is in this region.
30767      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30768      * @return {Boolean}
30769      */
30770     hasPanel : function(panel){
30771         if(typeof panel == "object"){ // must be panel obj
30772             panel = panel.getId();
30773         }
30774         return this.getPanel(panel) ? true : false;
30775     },
30776     
30777     /**
30778      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30779      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30780      * @param {Boolean} preservePanel Overrides the config preservePanel option
30781      * @return {Roo.ContentPanel} The panel that was removed
30782      */
30783     remove : function(panel, preservePanel){
30784         panel = this.getPanel(panel);
30785         if(!panel){
30786             return null;
30787         }
30788         var e = {};
30789         this.fireEvent("beforeremove", this, panel, e);
30790         if(e.cancel === true){
30791             return null;
30792         }
30793         var panelId = panel.getId();
30794         this.panels.removeKey(panelId);
30795         return panel;
30796     },
30797     
30798     /**
30799      * Returns the panel specified or null if it's not in this region.
30800      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30801      * @return {Roo.ContentPanel}
30802      */
30803     getPanel : function(id){
30804         if(typeof id == "object"){ // must be panel obj
30805             return id;
30806         }
30807         return this.panels.get(id);
30808     },
30809     
30810     /**
30811      * Returns this regions position (north/south/east/west/center).
30812      * @return {String} 
30813      */
30814     getPosition: function(){
30815         return this.position;    
30816     }
30817 });/*
30818  * Based on:
30819  * Ext JS Library 1.1.1
30820  * Copyright(c) 2006-2007, Ext JS, LLC.
30821  *
30822  * Originally Released Under LGPL - original licence link has changed is not relivant.
30823  *
30824  * Fork - LGPL
30825  * <script type="text/javascript">
30826  */
30827  
30828 /**
30829  * @class Roo.LayoutRegion
30830  * @extends Roo.BasicLayoutRegion
30831  * This class represents a region in a layout manager.
30832  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30833  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30834  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30835  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30836  * @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})
30837  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
30838  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30839  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30840  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30841  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30842  * @cfg {String}    title           The title for the region (overrides panel titles)
30843  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30844  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30845  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30846  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30847  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30848  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30849  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30850  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30851  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30852  * @cfg {Boolean}   showPin         True to show a pin button
30853  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30854  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30855  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30856  * @cfg {Number}    width           For East/West panels
30857  * @cfg {Number}    height          For North/South panels
30858  * @cfg {Boolean}   split           To show the splitter
30859  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30860  */
30861 Roo.LayoutRegion = function(mgr, config, pos){
30862     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30863     var dh = Roo.DomHelper;
30864     /** This region's container element 
30865     * @type Roo.Element */
30866     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30867     /** This region's title element 
30868     * @type Roo.Element */
30869
30870     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30871         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30872         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30873     ]}, true);
30874     this.titleEl.enableDisplayMode();
30875     /** This region's title text element 
30876     * @type HTMLElement */
30877     this.titleTextEl = this.titleEl.dom.firstChild;
30878     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30879     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30880     this.closeBtn.enableDisplayMode();
30881     this.closeBtn.on("click", this.closeClicked, this);
30882     this.closeBtn.hide();
30883
30884     this.createBody(config);
30885     this.visible = true;
30886     this.collapsed = false;
30887
30888     if(config.hideWhenEmpty){
30889         this.hide();
30890         this.on("paneladded", this.validateVisibility, this);
30891         this.on("panelremoved", this.validateVisibility, this);
30892     }
30893     this.applyConfig(config);
30894 };
30895
30896 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30897
30898     createBody : function(){
30899         /** This region's body element 
30900         * @type Roo.Element */
30901         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30902     },
30903
30904     applyConfig : function(c){
30905         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30906             var dh = Roo.DomHelper;
30907             if(c.titlebar !== false){
30908                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30909                 this.collapseBtn.on("click", this.collapse, this);
30910                 this.collapseBtn.enableDisplayMode();
30911
30912                 if(c.showPin === true || this.showPin){
30913                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30914                     this.stickBtn.enableDisplayMode();
30915                     this.stickBtn.on("click", this.expand, this);
30916                     this.stickBtn.hide();
30917                 }
30918             }
30919             /** This region's collapsed element
30920             * @type Roo.Element */
30921             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30922                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30923             ]}, true);
30924             if(c.floatable !== false){
30925                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30926                this.collapsedEl.on("click", this.collapseClick, this);
30927             }
30928
30929             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30930                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30931                    id: "message", unselectable: "on", style:{"float":"left"}});
30932                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30933              }
30934             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30935             this.expandBtn.on("click", this.expand, this);
30936         }
30937         if(this.collapseBtn){
30938             this.collapseBtn.setVisible(c.collapsible == true);
30939         }
30940         this.cmargins = c.cmargins || this.cmargins ||
30941                          (this.position == "west" || this.position == "east" ?
30942                              {top: 0, left: 2, right:2, bottom: 0} :
30943                              {top: 2, left: 0, right:0, bottom: 2});
30944         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30945         this.bottomTabs = c.tabPosition != "top";
30946         this.autoScroll = c.autoScroll || false;
30947         if(this.autoScroll){
30948             this.bodyEl.setStyle("overflow", "auto");
30949         }else{
30950             this.bodyEl.setStyle("overflow", "hidden");
30951         }
30952         //if(c.titlebar !== false){
30953             if((!c.titlebar && !c.title) || c.titlebar === false){
30954                 this.titleEl.hide();
30955             }else{
30956                 this.titleEl.show();
30957                 if(c.title){
30958                     this.titleTextEl.innerHTML = c.title;
30959                 }
30960             }
30961         //}
30962         this.duration = c.duration || .30;
30963         this.slideDuration = c.slideDuration || .45;
30964         this.config = c;
30965         if(c.collapsed){
30966             this.collapse(true);
30967         }
30968         if(c.hidden){
30969             this.hide();
30970         }
30971     },
30972     /**
30973      * Returns true if this region is currently visible.
30974      * @return {Boolean}
30975      */
30976     isVisible : function(){
30977         return this.visible;
30978     },
30979
30980     /**
30981      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30982      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30983      */
30984     setCollapsedTitle : function(title){
30985         title = title || "&#160;";
30986         if(this.collapsedTitleTextEl){
30987             this.collapsedTitleTextEl.innerHTML = title;
30988         }
30989     },
30990
30991     getBox : function(){
30992         var b;
30993         if(!this.collapsed){
30994             b = this.el.getBox(false, true);
30995         }else{
30996             b = this.collapsedEl.getBox(false, true);
30997         }
30998         return b;
30999     },
31000
31001     getMargins : function(){
31002         return this.collapsed ? this.cmargins : this.margins;
31003     },
31004
31005     highlight : function(){
31006         this.el.addClass("x-layout-panel-dragover");
31007     },
31008
31009     unhighlight : function(){
31010         this.el.removeClass("x-layout-panel-dragover");
31011     },
31012
31013     updateBox : function(box){
31014         this.box = box;
31015         if(!this.collapsed){
31016             this.el.dom.style.left = box.x + "px";
31017             this.el.dom.style.top = box.y + "px";
31018             this.updateBody(box.width, box.height);
31019         }else{
31020             this.collapsedEl.dom.style.left = box.x + "px";
31021             this.collapsedEl.dom.style.top = box.y + "px";
31022             this.collapsedEl.setSize(box.width, box.height);
31023         }
31024         if(this.tabs){
31025             this.tabs.autoSizeTabs();
31026         }
31027     },
31028
31029     updateBody : function(w, h){
31030         if(w !== null){
31031             this.el.setWidth(w);
31032             w -= this.el.getBorderWidth("rl");
31033             if(this.config.adjustments){
31034                 w += this.config.adjustments[0];
31035             }
31036         }
31037         if(h !== null){
31038             this.el.setHeight(h);
31039             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31040             h -= this.el.getBorderWidth("tb");
31041             if(this.config.adjustments){
31042                 h += this.config.adjustments[1];
31043             }
31044             this.bodyEl.setHeight(h);
31045             if(this.tabs){
31046                 h = this.tabs.syncHeight(h);
31047             }
31048         }
31049         if(this.panelSize){
31050             w = w !== null ? w : this.panelSize.width;
31051             h = h !== null ? h : this.panelSize.height;
31052         }
31053         if(this.activePanel){
31054             var el = this.activePanel.getEl();
31055             w = w !== null ? w : el.getWidth();
31056             h = h !== null ? h : el.getHeight();
31057             this.panelSize = {width: w, height: h};
31058             this.activePanel.setSize(w, h);
31059         }
31060         if(Roo.isIE && this.tabs){
31061             this.tabs.el.repaint();
31062         }
31063     },
31064
31065     /**
31066      * Returns the container element for this region.
31067      * @return {Roo.Element}
31068      */
31069     getEl : function(){
31070         return this.el;
31071     },
31072
31073     /**
31074      * Hides this region.
31075      */
31076     hide : function(){
31077         if(!this.collapsed){
31078             this.el.dom.style.left = "-2000px";
31079             this.el.hide();
31080         }else{
31081             this.collapsedEl.dom.style.left = "-2000px";
31082             this.collapsedEl.hide();
31083         }
31084         this.visible = false;
31085         this.fireEvent("visibilitychange", this, false);
31086     },
31087
31088     /**
31089      * Shows this region if it was previously hidden.
31090      */
31091     show : function(){
31092         if(!this.collapsed){
31093             this.el.show();
31094         }else{
31095             this.collapsedEl.show();
31096         }
31097         this.visible = true;
31098         this.fireEvent("visibilitychange", this, true);
31099     },
31100
31101     closeClicked : function(){
31102         if(this.activePanel){
31103             this.remove(this.activePanel);
31104         }
31105     },
31106
31107     collapseClick : function(e){
31108         if(this.isSlid){
31109            e.stopPropagation();
31110            this.slideIn();
31111         }else{
31112            e.stopPropagation();
31113            this.slideOut();
31114         }
31115     },
31116
31117     /**
31118      * Collapses this region.
31119      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31120      */
31121     collapse : function(skipAnim){
31122         if(this.collapsed) return;
31123         this.collapsed = true;
31124         if(this.split){
31125             this.split.el.hide();
31126         }
31127         if(this.config.animate && skipAnim !== true){
31128             this.fireEvent("invalidated", this);
31129             this.animateCollapse();
31130         }else{
31131             this.el.setLocation(-20000,-20000);
31132             this.el.hide();
31133             this.collapsedEl.show();
31134             this.fireEvent("collapsed", this);
31135             this.fireEvent("invalidated", this);
31136         }
31137     },
31138
31139     animateCollapse : function(){
31140         // overridden
31141     },
31142
31143     /**
31144      * Expands this region if it was previously collapsed.
31145      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31146      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31147      */
31148     expand : function(e, skipAnim){
31149         if(e) e.stopPropagation();
31150         if(!this.collapsed || this.el.hasActiveFx()) return;
31151         if(this.isSlid){
31152             this.afterSlideIn();
31153             skipAnim = true;
31154         }
31155         this.collapsed = false;
31156         if(this.config.animate && skipAnim !== true){
31157             this.animateExpand();
31158         }else{
31159             this.el.show();
31160             if(this.split){
31161                 this.split.el.show();
31162             }
31163             this.collapsedEl.setLocation(-2000,-2000);
31164             this.collapsedEl.hide();
31165             this.fireEvent("invalidated", this);
31166             this.fireEvent("expanded", this);
31167         }
31168     },
31169
31170     animateExpand : function(){
31171         // overridden
31172     },
31173
31174     initTabs : function()
31175     {
31176         this.bodyEl.setStyle("overflow", "hidden");
31177         var ts = new Roo.TabPanel(
31178                 this.bodyEl.dom,
31179                 {
31180                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31181                     disableTooltips: this.config.disableTabTips,
31182                     toolbar : this.config.toolbar
31183                 }
31184         );
31185         if(this.config.hideTabs){
31186             ts.stripWrap.setDisplayed(false);
31187         }
31188         this.tabs = ts;
31189         ts.resizeTabs = this.config.resizeTabs === true;
31190         ts.minTabWidth = this.config.minTabWidth || 40;
31191         ts.maxTabWidth = this.config.maxTabWidth || 250;
31192         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31193         ts.monitorResize = false;
31194         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31195         ts.bodyEl.addClass('x-layout-tabs-body');
31196         this.panels.each(this.initPanelAsTab, this);
31197     },
31198
31199     initPanelAsTab : function(panel){
31200         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31201                     this.config.closeOnTab && panel.isClosable());
31202         if(panel.tabTip !== undefined){
31203             ti.setTooltip(panel.tabTip);
31204         }
31205         ti.on("activate", function(){
31206               this.setActivePanel(panel);
31207         }, this);
31208         if(this.config.closeOnTab){
31209             ti.on("beforeclose", function(t, e){
31210                 e.cancel = true;
31211                 this.remove(panel);
31212             }, this);
31213         }
31214         return ti;
31215     },
31216
31217     updatePanelTitle : function(panel, title){
31218         if(this.activePanel == panel){
31219             this.updateTitle(title);
31220         }
31221         if(this.tabs){
31222             var ti = this.tabs.getTab(panel.getEl().id);
31223             ti.setText(title);
31224             if(panel.tabTip !== undefined){
31225                 ti.setTooltip(panel.tabTip);
31226             }
31227         }
31228     },
31229
31230     updateTitle : function(title){
31231         if(this.titleTextEl && !this.config.title){
31232             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31233         }
31234     },
31235
31236     setActivePanel : function(panel){
31237         panel = this.getPanel(panel);
31238         if(this.activePanel && this.activePanel != panel){
31239             this.activePanel.setActiveState(false);
31240         }
31241         this.activePanel = panel;
31242         panel.setActiveState(true);
31243         if(this.panelSize){
31244             panel.setSize(this.panelSize.width, this.panelSize.height);
31245         }
31246         if(this.closeBtn){
31247             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31248         }
31249         this.updateTitle(panel.getTitle());
31250         if(this.tabs){
31251             this.fireEvent("invalidated", this);
31252         }
31253         this.fireEvent("panelactivated", this, panel);
31254     },
31255
31256     /**
31257      * Shows the specified panel.
31258      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31259      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31260      */
31261     showPanel : function(panel){
31262         if(panel = this.getPanel(panel)){
31263             if(this.tabs){
31264                 var tab = this.tabs.getTab(panel.getEl().id);
31265                 if(tab.isHidden()){
31266                     this.tabs.unhideTab(tab.id);
31267                 }
31268                 tab.activate();
31269             }else{
31270                 this.setActivePanel(panel);
31271             }
31272         }
31273         return panel;
31274     },
31275
31276     /**
31277      * Get the active panel for this region.
31278      * @return {Roo.ContentPanel} The active panel or null
31279      */
31280     getActivePanel : function(){
31281         return this.activePanel;
31282     },
31283
31284     validateVisibility : function(){
31285         if(this.panels.getCount() < 1){
31286             this.updateTitle("&#160;");
31287             this.closeBtn.hide();
31288             this.hide();
31289         }else{
31290             if(!this.isVisible()){
31291                 this.show();
31292             }
31293         }
31294     },
31295
31296     /**
31297      * Adds the passed ContentPanel(s) to this region.
31298      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31299      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31300      */
31301     add : function(panel){
31302         if(arguments.length > 1){
31303             for(var i = 0, len = arguments.length; i < len; i++) {
31304                 this.add(arguments[i]);
31305             }
31306             return null;
31307         }
31308         if(this.hasPanel(panel)){
31309             this.showPanel(panel);
31310             return panel;
31311         }
31312         panel.setRegion(this);
31313         this.panels.add(panel);
31314         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31315             this.bodyEl.dom.appendChild(panel.getEl().dom);
31316             if(panel.background !== true){
31317                 this.setActivePanel(panel);
31318             }
31319             this.fireEvent("paneladded", this, panel);
31320             return panel;
31321         }
31322         if(!this.tabs){
31323             this.initTabs();
31324         }else{
31325             this.initPanelAsTab(panel);
31326         }
31327         if(panel.background !== true){
31328             this.tabs.activate(panel.getEl().id);
31329         }
31330         this.fireEvent("paneladded", this, panel);
31331         return panel;
31332     },
31333
31334     /**
31335      * Hides the tab for the specified panel.
31336      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31337      */
31338     hidePanel : function(panel){
31339         if(this.tabs && (panel = this.getPanel(panel))){
31340             this.tabs.hideTab(panel.getEl().id);
31341         }
31342     },
31343
31344     /**
31345      * Unhides the tab for a previously hidden panel.
31346      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31347      */
31348     unhidePanel : function(panel){
31349         if(this.tabs && (panel = this.getPanel(panel))){
31350             this.tabs.unhideTab(panel.getEl().id);
31351         }
31352     },
31353
31354     clearPanels : function(){
31355         while(this.panels.getCount() > 0){
31356              this.remove(this.panels.first());
31357         }
31358     },
31359
31360     /**
31361      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31362      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31363      * @param {Boolean} preservePanel Overrides the config preservePanel option
31364      * @return {Roo.ContentPanel} The panel that was removed
31365      */
31366     remove : function(panel, preservePanel){
31367         panel = this.getPanel(panel);
31368         if(!panel){
31369             return null;
31370         }
31371         var e = {};
31372         this.fireEvent("beforeremove", this, panel, e);
31373         if(e.cancel === true){
31374             return null;
31375         }
31376         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
31377         var panelId = panel.getId();
31378         this.panels.removeKey(panelId);
31379         if(preservePanel){
31380             document.body.appendChild(panel.getEl().dom);
31381         }
31382         if(this.tabs){
31383             this.tabs.removeTab(panel.getEl().id);
31384         }else if (!preservePanel){
31385             this.bodyEl.dom.removeChild(panel.getEl().dom);
31386         }
31387         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
31388             var p = this.panels.first();
31389             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
31390             tempEl.appendChild(p.getEl().dom);
31391             this.bodyEl.update("");
31392             this.bodyEl.dom.appendChild(p.getEl().dom);
31393             tempEl = null;
31394             this.updateTitle(p.getTitle());
31395             this.tabs = null;
31396             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31397             this.setActivePanel(p);
31398         }
31399         panel.setRegion(null);
31400         if(this.activePanel == panel){
31401             this.activePanel = null;
31402         }
31403         if(this.config.autoDestroy !== false && preservePanel !== true){
31404             try{panel.destroy();}catch(e){}
31405         }
31406         this.fireEvent("panelremoved", this, panel);
31407         return panel;
31408     },
31409
31410     /**
31411      * Returns the TabPanel component used by this region
31412      * @return {Roo.TabPanel}
31413      */
31414     getTabs : function(){
31415         return this.tabs;
31416     },
31417
31418     createTool : function(parentEl, className){
31419         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
31420             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
31421         btn.addClassOnOver("x-layout-tools-button-over");
31422         return btn;
31423     }
31424 });/*
31425  * Based on:
31426  * Ext JS Library 1.1.1
31427  * Copyright(c) 2006-2007, Ext JS, LLC.
31428  *
31429  * Originally Released Under LGPL - original licence link has changed is not relivant.
31430  *
31431  * Fork - LGPL
31432  * <script type="text/javascript">
31433  */
31434  
31435
31436
31437 /**
31438  * @class Roo.SplitLayoutRegion
31439  * @extends Roo.LayoutRegion
31440  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
31441  */
31442 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
31443     this.cursor = cursor;
31444     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
31445 };
31446
31447 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
31448     splitTip : "Drag to resize.",
31449     collapsibleSplitTip : "Drag to resize. Double click to hide.",
31450     useSplitTips : false,
31451
31452     applyConfig : function(config){
31453         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
31454         if(config.split){
31455             if(!this.split){
31456                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
31457                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
31458                 /** The SplitBar for this region 
31459                 * @type Roo.SplitBar */
31460                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
31461                 this.split.on("moved", this.onSplitMove, this);
31462                 this.split.useShim = config.useShim === true;
31463                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
31464                 if(this.useSplitTips){
31465                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
31466                 }
31467                 if(config.collapsible){
31468                     this.split.el.on("dblclick", this.collapse,  this);
31469                 }
31470             }
31471             if(typeof config.minSize != "undefined"){
31472                 this.split.minSize = config.minSize;
31473             }
31474             if(typeof config.maxSize != "undefined"){
31475                 this.split.maxSize = config.maxSize;
31476             }
31477             if(config.hideWhenEmpty || config.hidden || config.collapsed){
31478                 this.hideSplitter();
31479             }
31480         }
31481     },
31482
31483     getHMaxSize : function(){
31484          var cmax = this.config.maxSize || 10000;
31485          var center = this.mgr.getRegion("center");
31486          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
31487     },
31488
31489     getVMaxSize : function(){
31490          var cmax = this.config.maxSize || 10000;
31491          var center = this.mgr.getRegion("center");
31492          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
31493     },
31494
31495     onSplitMove : function(split, newSize){
31496         this.fireEvent("resized", this, newSize);
31497     },
31498     
31499     /** 
31500      * Returns the {@link Roo.SplitBar} for this region.
31501      * @return {Roo.SplitBar}
31502      */
31503     getSplitBar : function(){
31504         return this.split;
31505     },
31506     
31507     hide : function(){
31508         this.hideSplitter();
31509         Roo.SplitLayoutRegion.superclass.hide.call(this);
31510     },
31511
31512     hideSplitter : function(){
31513         if(this.split){
31514             this.split.el.setLocation(-2000,-2000);
31515             this.split.el.hide();
31516         }
31517     },
31518
31519     show : function(){
31520         if(this.split){
31521             this.split.el.show();
31522         }
31523         Roo.SplitLayoutRegion.superclass.show.call(this);
31524     },
31525     
31526     beforeSlide: function(){
31527         if(Roo.isGecko){// firefox overflow auto bug workaround
31528             this.bodyEl.clip();
31529             if(this.tabs) this.tabs.bodyEl.clip();
31530             if(this.activePanel){
31531                 this.activePanel.getEl().clip();
31532                 
31533                 if(this.activePanel.beforeSlide){
31534                     this.activePanel.beforeSlide();
31535                 }
31536             }
31537         }
31538     },
31539     
31540     afterSlide : function(){
31541         if(Roo.isGecko){// firefox overflow auto bug workaround
31542             this.bodyEl.unclip();
31543             if(this.tabs) this.tabs.bodyEl.unclip();
31544             if(this.activePanel){
31545                 this.activePanel.getEl().unclip();
31546                 if(this.activePanel.afterSlide){
31547                     this.activePanel.afterSlide();
31548                 }
31549             }
31550         }
31551     },
31552
31553     initAutoHide : function(){
31554         if(this.autoHide !== false){
31555             if(!this.autoHideHd){
31556                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31557                 this.autoHideHd = {
31558                     "mouseout": function(e){
31559                         if(!e.within(this.el, true)){
31560                             st.delay(500);
31561                         }
31562                     },
31563                     "mouseover" : function(e){
31564                         st.cancel();
31565                     },
31566                     scope : this
31567                 };
31568             }
31569             this.el.on(this.autoHideHd);
31570         }
31571     },
31572
31573     clearAutoHide : function(){
31574         if(this.autoHide !== false){
31575             this.el.un("mouseout", this.autoHideHd.mouseout);
31576             this.el.un("mouseover", this.autoHideHd.mouseover);
31577         }
31578     },
31579
31580     clearMonitor : function(){
31581         Roo.get(document).un("click", this.slideInIf, this);
31582     },
31583
31584     // these names are backwards but not changed for compat
31585     slideOut : function(){
31586         if(this.isSlid || this.el.hasActiveFx()){
31587             return;
31588         }
31589         this.isSlid = true;
31590         if(this.collapseBtn){
31591             this.collapseBtn.hide();
31592         }
31593         this.closeBtnState = this.closeBtn.getStyle('display');
31594         this.closeBtn.hide();
31595         if(this.stickBtn){
31596             this.stickBtn.show();
31597         }
31598         this.el.show();
31599         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31600         this.beforeSlide();
31601         this.el.setStyle("z-index", 10001);
31602         this.el.slideIn(this.getSlideAnchor(), {
31603             callback: function(){
31604                 this.afterSlide();
31605                 this.initAutoHide();
31606                 Roo.get(document).on("click", this.slideInIf, this);
31607                 this.fireEvent("slideshow", this);
31608             },
31609             scope: this,
31610             block: true
31611         });
31612     },
31613
31614     afterSlideIn : function(){
31615         this.clearAutoHide();
31616         this.isSlid = false;
31617         this.clearMonitor();
31618         this.el.setStyle("z-index", "");
31619         if(this.collapseBtn){
31620             this.collapseBtn.show();
31621         }
31622         this.closeBtn.setStyle('display', this.closeBtnState);
31623         if(this.stickBtn){
31624             this.stickBtn.hide();
31625         }
31626         this.fireEvent("slidehide", this);
31627     },
31628
31629     slideIn : function(cb){
31630         if(!this.isSlid || this.el.hasActiveFx()){
31631             Roo.callback(cb);
31632             return;
31633         }
31634         this.isSlid = false;
31635         this.beforeSlide();
31636         this.el.slideOut(this.getSlideAnchor(), {
31637             callback: function(){
31638                 this.el.setLeftTop(-10000, -10000);
31639                 this.afterSlide();
31640                 this.afterSlideIn();
31641                 Roo.callback(cb);
31642             },
31643             scope: this,
31644             block: true
31645         });
31646     },
31647     
31648     slideInIf : function(e){
31649         if(!e.within(this.el)){
31650             this.slideIn();
31651         }
31652     },
31653
31654     animateCollapse : function(){
31655         this.beforeSlide();
31656         this.el.setStyle("z-index", 20000);
31657         var anchor = this.getSlideAnchor();
31658         this.el.slideOut(anchor, {
31659             callback : function(){
31660                 this.el.setStyle("z-index", "");
31661                 this.collapsedEl.slideIn(anchor, {duration:.3});
31662                 this.afterSlide();
31663                 this.el.setLocation(-10000,-10000);
31664                 this.el.hide();
31665                 this.fireEvent("collapsed", this);
31666             },
31667             scope: this,
31668             block: true
31669         });
31670     },
31671
31672     animateExpand : function(){
31673         this.beforeSlide();
31674         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31675         this.el.setStyle("z-index", 20000);
31676         this.collapsedEl.hide({
31677             duration:.1
31678         });
31679         this.el.slideIn(this.getSlideAnchor(), {
31680             callback : function(){
31681                 this.el.setStyle("z-index", "");
31682                 this.afterSlide();
31683                 if(this.split){
31684                     this.split.el.show();
31685                 }
31686                 this.fireEvent("invalidated", this);
31687                 this.fireEvent("expanded", this);
31688             },
31689             scope: this,
31690             block: true
31691         });
31692     },
31693
31694     anchors : {
31695         "west" : "left",
31696         "east" : "right",
31697         "north" : "top",
31698         "south" : "bottom"
31699     },
31700
31701     sanchors : {
31702         "west" : "l",
31703         "east" : "r",
31704         "north" : "t",
31705         "south" : "b"
31706     },
31707
31708     canchors : {
31709         "west" : "tl-tr",
31710         "east" : "tr-tl",
31711         "north" : "tl-bl",
31712         "south" : "bl-tl"
31713     },
31714
31715     getAnchor : function(){
31716         return this.anchors[this.position];
31717     },
31718
31719     getCollapseAnchor : function(){
31720         return this.canchors[this.position];
31721     },
31722
31723     getSlideAnchor : function(){
31724         return this.sanchors[this.position];
31725     },
31726
31727     getAlignAdj : function(){
31728         var cm = this.cmargins;
31729         switch(this.position){
31730             case "west":
31731                 return [0, 0];
31732             break;
31733             case "east":
31734                 return [0, 0];
31735             break;
31736             case "north":
31737                 return [0, 0];
31738             break;
31739             case "south":
31740                 return [0, 0];
31741             break;
31742         }
31743     },
31744
31745     getExpandAdj : function(){
31746         var c = this.collapsedEl, cm = this.cmargins;
31747         switch(this.position){
31748             case "west":
31749                 return [-(cm.right+c.getWidth()+cm.left), 0];
31750             break;
31751             case "east":
31752                 return [cm.right+c.getWidth()+cm.left, 0];
31753             break;
31754             case "north":
31755                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31756             break;
31757             case "south":
31758                 return [0, cm.top+cm.bottom+c.getHeight()];
31759             break;
31760         }
31761     }
31762 });/*
31763  * Based on:
31764  * Ext JS Library 1.1.1
31765  * Copyright(c) 2006-2007, Ext JS, LLC.
31766  *
31767  * Originally Released Under LGPL - original licence link has changed is not relivant.
31768  *
31769  * Fork - LGPL
31770  * <script type="text/javascript">
31771  */
31772 /*
31773  * These classes are private internal classes
31774  */
31775 Roo.CenterLayoutRegion = function(mgr, config){
31776     Roo.LayoutRegion.call(this, mgr, config, "center");
31777     this.visible = true;
31778     this.minWidth = config.minWidth || 20;
31779     this.minHeight = config.minHeight || 20;
31780 };
31781
31782 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31783     hide : function(){
31784         // center panel can't be hidden
31785     },
31786     
31787     show : function(){
31788         // center panel can't be hidden
31789     },
31790     
31791     getMinWidth: function(){
31792         return this.minWidth;
31793     },
31794     
31795     getMinHeight: function(){
31796         return this.minHeight;
31797     }
31798 });
31799
31800
31801 Roo.NorthLayoutRegion = function(mgr, config){
31802     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31803     if(this.split){
31804         this.split.placement = Roo.SplitBar.TOP;
31805         this.split.orientation = Roo.SplitBar.VERTICAL;
31806         this.split.el.addClass("x-layout-split-v");
31807     }
31808     var size = config.initialSize || config.height;
31809     if(typeof size != "undefined"){
31810         this.el.setHeight(size);
31811     }
31812 };
31813 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31814     orientation: Roo.SplitBar.VERTICAL,
31815     getBox : function(){
31816         if(this.collapsed){
31817             return this.collapsedEl.getBox();
31818         }
31819         var box = this.el.getBox();
31820         if(this.split){
31821             box.height += this.split.el.getHeight();
31822         }
31823         return box;
31824     },
31825     
31826     updateBox : function(box){
31827         if(this.split && !this.collapsed){
31828             box.height -= this.split.el.getHeight();
31829             this.split.el.setLeft(box.x);
31830             this.split.el.setTop(box.y+box.height);
31831             this.split.el.setWidth(box.width);
31832         }
31833         if(this.collapsed){
31834             this.updateBody(box.width, null);
31835         }
31836         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31837     }
31838 });
31839
31840 Roo.SouthLayoutRegion = function(mgr, config){
31841     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31842     if(this.split){
31843         this.split.placement = Roo.SplitBar.BOTTOM;
31844         this.split.orientation = Roo.SplitBar.VERTICAL;
31845         this.split.el.addClass("x-layout-split-v");
31846     }
31847     var size = config.initialSize || config.height;
31848     if(typeof size != "undefined"){
31849         this.el.setHeight(size);
31850     }
31851 };
31852 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31853     orientation: Roo.SplitBar.VERTICAL,
31854     getBox : function(){
31855         if(this.collapsed){
31856             return this.collapsedEl.getBox();
31857         }
31858         var box = this.el.getBox();
31859         if(this.split){
31860             var sh = this.split.el.getHeight();
31861             box.height += sh;
31862             box.y -= sh;
31863         }
31864         return box;
31865     },
31866     
31867     updateBox : function(box){
31868         if(this.split && !this.collapsed){
31869             var sh = this.split.el.getHeight();
31870             box.height -= sh;
31871             box.y += sh;
31872             this.split.el.setLeft(box.x);
31873             this.split.el.setTop(box.y-sh);
31874             this.split.el.setWidth(box.width);
31875         }
31876         if(this.collapsed){
31877             this.updateBody(box.width, null);
31878         }
31879         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31880     }
31881 });
31882
31883 Roo.EastLayoutRegion = function(mgr, config){
31884     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31885     if(this.split){
31886         this.split.placement = Roo.SplitBar.RIGHT;
31887         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31888         this.split.el.addClass("x-layout-split-h");
31889     }
31890     var size = config.initialSize || config.width;
31891     if(typeof size != "undefined"){
31892         this.el.setWidth(size);
31893     }
31894 };
31895 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31896     orientation: Roo.SplitBar.HORIZONTAL,
31897     getBox : function(){
31898         if(this.collapsed){
31899             return this.collapsedEl.getBox();
31900         }
31901         var box = this.el.getBox();
31902         if(this.split){
31903             var sw = this.split.el.getWidth();
31904             box.width += sw;
31905             box.x -= sw;
31906         }
31907         return box;
31908     },
31909
31910     updateBox : function(box){
31911         if(this.split && !this.collapsed){
31912             var sw = this.split.el.getWidth();
31913             box.width -= sw;
31914             this.split.el.setLeft(box.x);
31915             this.split.el.setTop(box.y);
31916             this.split.el.setHeight(box.height);
31917             box.x += sw;
31918         }
31919         if(this.collapsed){
31920             this.updateBody(null, box.height);
31921         }
31922         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31923     }
31924 });
31925
31926 Roo.WestLayoutRegion = function(mgr, config){
31927     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31928     if(this.split){
31929         this.split.placement = Roo.SplitBar.LEFT;
31930         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31931         this.split.el.addClass("x-layout-split-h");
31932     }
31933     var size = config.initialSize || config.width;
31934     if(typeof size != "undefined"){
31935         this.el.setWidth(size);
31936     }
31937 };
31938 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31939     orientation: Roo.SplitBar.HORIZONTAL,
31940     getBox : function(){
31941         if(this.collapsed){
31942             return this.collapsedEl.getBox();
31943         }
31944         var box = this.el.getBox();
31945         if(this.split){
31946             box.width += this.split.el.getWidth();
31947         }
31948         return box;
31949     },
31950     
31951     updateBox : function(box){
31952         if(this.split && !this.collapsed){
31953             var sw = this.split.el.getWidth();
31954             box.width -= sw;
31955             this.split.el.setLeft(box.x+box.width);
31956             this.split.el.setTop(box.y);
31957             this.split.el.setHeight(box.height);
31958         }
31959         if(this.collapsed){
31960             this.updateBody(null, box.height);
31961         }
31962         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31963     }
31964 });
31965 /*
31966  * Based on:
31967  * Ext JS Library 1.1.1
31968  * Copyright(c) 2006-2007, Ext JS, LLC.
31969  *
31970  * Originally Released Under LGPL - original licence link has changed is not relivant.
31971  *
31972  * Fork - LGPL
31973  * <script type="text/javascript">
31974  */
31975  
31976  
31977 /*
31978  * Private internal class for reading and applying state
31979  */
31980 Roo.LayoutStateManager = function(layout){
31981      // default empty state
31982      this.state = {
31983         north: {},
31984         south: {},
31985         east: {},
31986         west: {}       
31987     };
31988 };
31989
31990 Roo.LayoutStateManager.prototype = {
31991     init : function(layout, provider){
31992         this.provider = provider;
31993         var state = provider.get(layout.id+"-layout-state");
31994         if(state){
31995             var wasUpdating = layout.isUpdating();
31996             if(!wasUpdating){
31997                 layout.beginUpdate();
31998             }
31999             for(var key in state){
32000                 if(typeof state[key] != "function"){
32001                     var rstate = state[key];
32002                     var r = layout.getRegion(key);
32003                     if(r && rstate){
32004                         if(rstate.size){
32005                             r.resizeTo(rstate.size);
32006                         }
32007                         if(rstate.collapsed == true){
32008                             r.collapse(true);
32009                         }else{
32010                             r.expand(null, true);
32011                         }
32012                     }
32013                 }
32014             }
32015             if(!wasUpdating){
32016                 layout.endUpdate();
32017             }
32018             this.state = state; 
32019         }
32020         this.layout = layout;
32021         layout.on("regionresized", this.onRegionResized, this);
32022         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32023         layout.on("regionexpanded", this.onRegionExpanded, this);
32024     },
32025     
32026     storeState : function(){
32027         this.provider.set(this.layout.id+"-layout-state", this.state);
32028     },
32029     
32030     onRegionResized : function(region, newSize){
32031         this.state[region.getPosition()].size = newSize;
32032         this.storeState();
32033     },
32034     
32035     onRegionCollapsed : function(region){
32036         this.state[region.getPosition()].collapsed = true;
32037         this.storeState();
32038     },
32039     
32040     onRegionExpanded : function(region){
32041         this.state[region.getPosition()].collapsed = false;
32042         this.storeState();
32043     }
32044 };/*
32045  * Based on:
32046  * Ext JS Library 1.1.1
32047  * Copyright(c) 2006-2007, Ext JS, LLC.
32048  *
32049  * Originally Released Under LGPL - original licence link has changed is not relivant.
32050  *
32051  * Fork - LGPL
32052  * <script type="text/javascript">
32053  */
32054 /**
32055  * @class Roo.ContentPanel
32056  * @extends Roo.util.Observable
32057  * A basic ContentPanel element.
32058  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32059  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32060  * @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
32061  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32062  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32063  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32064  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32065  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32066  * @cfg {String} title          The title for this panel
32067  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32068  * @cfg {String} url            Calls {@link #setUrl} with this value
32069  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32070  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32071  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32072  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32073
32074  * @constructor
32075  * Create a new ContentPanel.
32076  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32077  * @param {String/Object} config A string to set only the title or a config object
32078  * @param {String} content (optional) Set the HTML content for this panel
32079  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32080  */
32081 Roo.ContentPanel = function(el, config, content){
32082     
32083      
32084     /*
32085     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32086         config = el;
32087         el = Roo.id();
32088     }
32089     if (config && config.parentLayout) { 
32090         el = config.parentLayout.el.createChild(); 
32091     }
32092     */
32093     if(el.autoCreate){ // xtype is available if this is called from factory
32094         config = el;
32095         el = Roo.id();
32096     }
32097     this.el = Roo.get(el);
32098     if(!this.el && config && config.autoCreate){
32099         if(typeof config.autoCreate == "object"){
32100             if(!config.autoCreate.id){
32101                 config.autoCreate.id = config.id||el;
32102             }
32103             this.el = Roo.DomHelper.append(document.body,
32104                         config.autoCreate, true);
32105         }else{
32106             this.el = Roo.DomHelper.append(document.body,
32107                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32108         }
32109     }
32110     this.closable = false;
32111     this.loaded = false;
32112     this.active = false;
32113     if(typeof config == "string"){
32114         this.title = config;
32115     }else{
32116         Roo.apply(this, config);
32117     }
32118     
32119     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32120         this.wrapEl = this.el.wrap();
32121         this.toolbar.container = this.el.insertSibling(false, 'before');
32122         this.toolbar = new Roo.Toolbar(this.toolbar);
32123     }
32124     
32125     
32126     
32127     if(this.resizeEl){
32128         this.resizeEl = Roo.get(this.resizeEl, true);
32129     }else{
32130         this.resizeEl = this.el;
32131     }
32132     this.addEvents({
32133         /**
32134          * @event activate
32135          * Fires when this panel is activated. 
32136          * @param {Roo.ContentPanel} this
32137          */
32138         "activate" : true,
32139         /**
32140          * @event deactivate
32141          * Fires when this panel is activated. 
32142          * @param {Roo.ContentPanel} this
32143          */
32144         "deactivate" : true,
32145
32146         /**
32147          * @event resize
32148          * Fires when this panel is resized if fitToFrame is true.
32149          * @param {Roo.ContentPanel} this
32150          * @param {Number} width The width after any component adjustments
32151          * @param {Number} height The height after any component adjustments
32152          */
32153         "resize" : true,
32154         
32155          /**
32156          * @event render
32157          * Fires when this tab is created
32158          * @param {Roo.ContentPanel} this
32159          */
32160         "render" : true
32161         
32162         
32163         
32164     });
32165     if(this.autoScroll){
32166         this.resizeEl.setStyle("overflow", "auto");
32167     } else {
32168         // fix randome scrolling
32169         this.el.on('scroll', function() {
32170             Roo.log('fix random scolling');
32171             this.scrollTo('top',0); 
32172         });
32173     }
32174     content = content || this.content;
32175     if(content){
32176         this.setContent(content);
32177     }
32178     if(config && config.url){
32179         this.setUrl(this.url, this.params, this.loadOnce);
32180     }
32181     
32182     
32183     
32184     Roo.ContentPanel.superclass.constructor.call(this);
32185     
32186     this.fireEvent('render', this);
32187 };
32188
32189 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32190     tabTip:'',
32191     setRegion : function(region){
32192         this.region = region;
32193         if(region){
32194            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32195         }else{
32196            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32197         } 
32198     },
32199     
32200     /**
32201      * Returns the toolbar for this Panel if one was configured. 
32202      * @return {Roo.Toolbar} 
32203      */
32204     getToolbar : function(){
32205         return this.toolbar;
32206     },
32207     
32208     setActiveState : function(active){
32209         this.active = active;
32210         if(!active){
32211             this.fireEvent("deactivate", this);
32212         }else{
32213             this.fireEvent("activate", this);
32214         }
32215     },
32216     /**
32217      * Updates this panel's element
32218      * @param {String} content The new content
32219      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32220     */
32221     setContent : function(content, loadScripts){
32222         this.el.update(content, loadScripts);
32223     },
32224
32225     ignoreResize : function(w, h){
32226         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32227             return true;
32228         }else{
32229             this.lastSize = {width: w, height: h};
32230             return false;
32231         }
32232     },
32233     /**
32234      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32235      * @return {Roo.UpdateManager} The UpdateManager
32236      */
32237     getUpdateManager : function(){
32238         return this.el.getUpdateManager();
32239     },
32240      /**
32241      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32242      * @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:
32243 <pre><code>
32244 panel.load({
32245     url: "your-url.php",
32246     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32247     callback: yourFunction,
32248     scope: yourObject, //(optional scope)
32249     discardUrl: false,
32250     nocache: false,
32251     text: "Loading...",
32252     timeout: 30,
32253     scripts: false
32254 });
32255 </code></pre>
32256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32257      * 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.
32258      * @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}
32259      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32260      * @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.
32261      * @return {Roo.ContentPanel} this
32262      */
32263     load : function(){
32264         var um = this.el.getUpdateManager();
32265         um.update.apply(um, arguments);
32266         return this;
32267     },
32268
32269
32270     /**
32271      * 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.
32272      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32273      * @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)
32274      * @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)
32275      * @return {Roo.UpdateManager} The UpdateManager
32276      */
32277     setUrl : function(url, params, loadOnce){
32278         if(this.refreshDelegate){
32279             this.removeListener("activate", this.refreshDelegate);
32280         }
32281         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32282         this.on("activate", this.refreshDelegate);
32283         return this.el.getUpdateManager();
32284     },
32285     
32286     _handleRefresh : function(url, params, loadOnce){
32287         if(!loadOnce || !this.loaded){
32288             var updater = this.el.getUpdateManager();
32289             updater.update(url, params, this._setLoaded.createDelegate(this));
32290         }
32291     },
32292     
32293     _setLoaded : function(){
32294         this.loaded = true;
32295     }, 
32296     
32297     /**
32298      * Returns this panel's id
32299      * @return {String} 
32300      */
32301     getId : function(){
32302         return this.el.id;
32303     },
32304     
32305     /** 
32306      * Returns this panel's element - used by regiosn to add.
32307      * @return {Roo.Element} 
32308      */
32309     getEl : function(){
32310         return this.wrapEl || this.el;
32311     },
32312     
32313     adjustForComponents : function(width, height){
32314         if(this.resizeEl != this.el){
32315             width -= this.el.getFrameWidth('lr');
32316             height -= this.el.getFrameWidth('tb');
32317         }
32318         if(this.toolbar){
32319             var te = this.toolbar.getEl();
32320             height -= te.getHeight();
32321             te.setWidth(width);
32322         }
32323         if(this.adjustments){
32324             width += this.adjustments[0];
32325             height += this.adjustments[1];
32326         }
32327         return {"width": width, "height": height};
32328     },
32329     
32330     setSize : function(width, height){
32331         if(this.fitToFrame && !this.ignoreResize(width, height)){
32332             if(this.fitContainer && this.resizeEl != this.el){
32333                 this.el.setSize(width, height);
32334             }
32335             var size = this.adjustForComponents(width, height);
32336             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
32337             this.fireEvent('resize', this, size.width, size.height);
32338         }
32339     },
32340     
32341     /**
32342      * Returns this panel's title
32343      * @return {String} 
32344      */
32345     getTitle : function(){
32346         return this.title;
32347     },
32348     
32349     /**
32350      * Set this panel's title
32351      * @param {String} title
32352      */
32353     setTitle : function(title){
32354         this.title = title;
32355         if(this.region){
32356             this.region.updatePanelTitle(this, title);
32357         }
32358     },
32359     
32360     /**
32361      * Returns true is this panel was configured to be closable
32362      * @return {Boolean} 
32363      */
32364     isClosable : function(){
32365         return this.closable;
32366     },
32367     
32368     beforeSlide : function(){
32369         this.el.clip();
32370         this.resizeEl.clip();
32371     },
32372     
32373     afterSlide : function(){
32374         this.el.unclip();
32375         this.resizeEl.unclip();
32376     },
32377     
32378     /**
32379      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
32380      *   Will fail silently if the {@link #setUrl} method has not been called.
32381      *   This does not activate the panel, just updates its content.
32382      */
32383     refresh : function(){
32384         if(this.refreshDelegate){
32385            this.loaded = false;
32386            this.refreshDelegate();
32387         }
32388     },
32389     
32390     /**
32391      * Destroys this panel
32392      */
32393     destroy : function(){
32394         this.el.removeAllListeners();
32395         var tempEl = document.createElement("span");
32396         tempEl.appendChild(this.el.dom);
32397         tempEl.innerHTML = "";
32398         this.el.remove();
32399         this.el = null;
32400     },
32401     
32402     /**
32403      * form - if the content panel contains a form - this is a reference to it.
32404      * @type {Roo.form.Form}
32405      */
32406     form : false,
32407     /**
32408      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
32409      *    This contains a reference to it.
32410      * @type {Roo.View}
32411      */
32412     view : false,
32413     
32414       /**
32415      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
32416      * <pre><code>
32417
32418 layout.addxtype({
32419        xtype : 'Form',
32420        items: [ .... ]
32421    }
32422 );
32423
32424 </code></pre>
32425      * @param {Object} cfg Xtype definition of item to add.
32426      */
32427     
32428     addxtype : function(cfg) {
32429         // add form..
32430         if (cfg.xtype.match(/^Form$/)) {
32431             var el = this.el.createChild();
32432
32433             this.form = new  Roo.form.Form(cfg);
32434             
32435             
32436             if ( this.form.allItems.length) this.form.render(el.dom);
32437             return this.form;
32438         }
32439         // should only have one of theses..
32440         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
32441             // views..
32442             cfg.el = this.el.appendChild(document.createElement("div"));
32443             // factory?
32444             
32445             var ret = new Roo.factory(cfg);
32446             ret.render && ret.render(false, ''); // render blank..
32447             this.view = ret;
32448             return ret;
32449         }
32450         return false;
32451     }
32452 });
32453
32454 /**
32455  * @class Roo.GridPanel
32456  * @extends Roo.ContentPanel
32457  * @constructor
32458  * Create a new GridPanel.
32459  * @param {Roo.grid.Grid} grid The grid for this panel
32460  * @param {String/Object} config A string to set only the panel's title, or a config object
32461  */
32462 Roo.GridPanel = function(grid, config){
32463     
32464   
32465     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32466         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32467         
32468     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32469     
32470     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32471     
32472     if(this.toolbar){
32473         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32474     }
32475     // xtype created footer. - not sure if will work as we normally have to render first..
32476     if (this.footer && !this.footer.el && this.footer.xtype) {
32477         
32478         this.footer.container = this.grid.getView().getFooterPanel(true);
32479         this.footer.dataSource = this.grid.dataSource;
32480         this.footer = Roo.factory(this.footer, Roo);
32481         
32482     }
32483     
32484     grid.monitorWindowResize = false; // turn off autosizing
32485     grid.autoHeight = false;
32486     grid.autoWidth = false;
32487     this.grid = grid;
32488     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32489 };
32490
32491 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32492     getId : function(){
32493         return this.grid.id;
32494     },
32495     
32496     /**
32497      * Returns the grid for this panel
32498      * @return {Roo.grid.Grid} 
32499      */
32500     getGrid : function(){
32501         return this.grid;    
32502     },
32503     
32504     setSize : function(width, height){
32505         if(!this.ignoreResize(width, height)){
32506             var grid = this.grid;
32507             var size = this.adjustForComponents(width, height);
32508             grid.getGridEl().setSize(size.width, size.height);
32509             grid.autoSize();
32510         }
32511     },
32512     
32513     beforeSlide : function(){
32514         this.grid.getView().scroller.clip();
32515     },
32516     
32517     afterSlide : function(){
32518         this.grid.getView().scroller.unclip();
32519     },
32520     
32521     destroy : function(){
32522         this.grid.destroy();
32523         delete this.grid;
32524         Roo.GridPanel.superclass.destroy.call(this); 
32525     }
32526 });
32527
32528
32529 /**
32530  * @class Roo.NestedLayoutPanel
32531  * @extends Roo.ContentPanel
32532  * @constructor
32533  * Create a new NestedLayoutPanel.
32534  * 
32535  * 
32536  * @param {Roo.BorderLayout} layout The layout for this panel
32537  * @param {String/Object} config A string to set only the title or a config object
32538  */
32539 Roo.NestedLayoutPanel = function(layout, config)
32540 {
32541     // construct with only one argument..
32542     /* FIXME - implement nicer consturctors
32543     if (layout.layout) {
32544         config = layout;
32545         layout = config.layout;
32546         delete config.layout;
32547     }
32548     if (layout.xtype && !layout.getEl) {
32549         // then layout needs constructing..
32550         layout = Roo.factory(layout, Roo);
32551     }
32552     */
32553     
32554     
32555     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32556     
32557     layout.monitorWindowResize = false; // turn off autosizing
32558     this.layout = layout;
32559     this.layout.getEl().addClass("x-layout-nested-layout");
32560     
32561     
32562     
32563     
32564 };
32565
32566 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32567
32568     setSize : function(width, height){
32569         if(!this.ignoreResize(width, height)){
32570             var size = this.adjustForComponents(width, height);
32571             var el = this.layout.getEl();
32572             el.setSize(size.width, size.height);
32573             var touch = el.dom.offsetWidth;
32574             this.layout.layout();
32575             // ie requires a double layout on the first pass
32576             if(Roo.isIE && !this.initialized){
32577                 this.initialized = true;
32578                 this.layout.layout();
32579             }
32580         }
32581     },
32582     
32583     // activate all subpanels if not currently active..
32584     
32585     setActiveState : function(active){
32586         this.active = active;
32587         if(!active){
32588             this.fireEvent("deactivate", this);
32589             return;
32590         }
32591         
32592         this.fireEvent("activate", this);
32593         // not sure if this should happen before or after..
32594         if (!this.layout) {
32595             return; // should not happen..
32596         }
32597         var reg = false;
32598         for (var r in this.layout.regions) {
32599             reg = this.layout.getRegion(r);
32600             if (reg.getActivePanel()) {
32601                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32602                 reg.setActivePanel(reg.getActivePanel());
32603                 continue;
32604             }
32605             if (!reg.panels.length) {
32606                 continue;
32607             }
32608             reg.showPanel(reg.getPanel(0));
32609         }
32610         
32611         
32612         
32613         
32614     },
32615     
32616     /**
32617      * Returns the nested BorderLayout for this panel
32618      * @return {Roo.BorderLayout} 
32619      */
32620     getLayout : function(){
32621         return this.layout;
32622     },
32623     
32624      /**
32625      * Adds a xtype elements to the layout of the nested panel
32626      * <pre><code>
32627
32628 panel.addxtype({
32629        xtype : 'ContentPanel',
32630        region: 'west',
32631        items: [ .... ]
32632    }
32633 );
32634
32635 panel.addxtype({
32636         xtype : 'NestedLayoutPanel',
32637         region: 'west',
32638         layout: {
32639            center: { },
32640            west: { }   
32641         },
32642         items : [ ... list of content panels or nested layout panels.. ]
32643    }
32644 );
32645 </code></pre>
32646      * @param {Object} cfg Xtype definition of item to add.
32647      */
32648     addxtype : function(cfg) {
32649         return this.layout.addxtype(cfg);
32650     
32651     }
32652 });
32653
32654 Roo.ScrollPanel = function(el, config, content){
32655     config = config || {};
32656     config.fitToFrame = true;
32657     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32658     
32659     this.el.dom.style.overflow = "hidden";
32660     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32661     this.el.removeClass("x-layout-inactive-content");
32662     this.el.on("mousewheel", this.onWheel, this);
32663
32664     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32665     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32666     up.unselectable(); down.unselectable();
32667     up.on("click", this.scrollUp, this);
32668     down.on("click", this.scrollDown, this);
32669     up.addClassOnOver("x-scroller-btn-over");
32670     down.addClassOnOver("x-scroller-btn-over");
32671     up.addClassOnClick("x-scroller-btn-click");
32672     down.addClassOnClick("x-scroller-btn-click");
32673     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32674
32675     this.resizeEl = this.el;
32676     this.el = wrap; this.up = up; this.down = down;
32677 };
32678
32679 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32680     increment : 100,
32681     wheelIncrement : 5,
32682     scrollUp : function(){
32683         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32684     },
32685
32686     scrollDown : function(){
32687         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32688     },
32689
32690     afterScroll : function(){
32691         var el = this.resizeEl;
32692         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32693         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32694         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32695     },
32696
32697     setSize : function(){
32698         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32699         this.afterScroll();
32700     },
32701
32702     onWheel : function(e){
32703         var d = e.getWheelDelta();
32704         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32705         this.afterScroll();
32706         e.stopEvent();
32707     },
32708
32709     setContent : function(content, loadScripts){
32710         this.resizeEl.update(content, loadScripts);
32711     }
32712
32713 });
32714
32715
32716
32717
32718
32719
32720
32721
32722
32723 /**
32724  * @class Roo.TreePanel
32725  * @extends Roo.ContentPanel
32726  * @constructor
32727  * Create a new TreePanel. - defaults to fit/scoll contents.
32728  * @param {String/Object} config A string to set only the panel's title, or a config object
32729  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32730  */
32731 Roo.TreePanel = function(config){
32732     var el = config.el;
32733     var tree = config.tree;
32734     delete config.tree; 
32735     delete config.el; // hopefull!
32736     
32737     // wrapper for IE7 strict & safari scroll issue
32738     
32739     var treeEl = el.createChild();
32740     config.resizeEl = treeEl;
32741     
32742     
32743     
32744     Roo.TreePanel.superclass.constructor.call(this, el, config);
32745  
32746  
32747     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32748     //console.log(tree);
32749     this.on('activate', function()
32750     {
32751         if (this.tree.rendered) {
32752             return;
32753         }
32754         //console.log('render tree');
32755         this.tree.render();
32756     });
32757     
32758     this.on('resize',  function (cp, w, h) {
32759             this.tree.innerCt.setWidth(w);
32760             this.tree.innerCt.setHeight(h);
32761             this.tree.innerCt.setStyle('overflow-y', 'auto');
32762     });
32763
32764         
32765     
32766 };
32767
32768 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32769     fitToFrame : true,
32770     autoScroll : true
32771 });
32772
32773
32774
32775
32776
32777
32778
32779
32780
32781
32782
32783 /*
32784  * Based on:
32785  * Ext JS Library 1.1.1
32786  * Copyright(c) 2006-2007, Ext JS, LLC.
32787  *
32788  * Originally Released Under LGPL - original licence link has changed is not relivant.
32789  *
32790  * Fork - LGPL
32791  * <script type="text/javascript">
32792  */
32793  
32794
32795 /**
32796  * @class Roo.ReaderLayout
32797  * @extends Roo.BorderLayout
32798  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32799  * center region containing two nested regions (a top one for a list view and one for item preview below),
32800  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32801  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32802  * expedites the setup of the overall layout and regions for this common application style.
32803  * Example:
32804  <pre><code>
32805 var reader = new Roo.ReaderLayout();
32806 var CP = Roo.ContentPanel;  // shortcut for adding
32807
32808 reader.beginUpdate();
32809 reader.add("north", new CP("north", "North"));
32810 reader.add("west", new CP("west", {title: "West"}));
32811 reader.add("east", new CP("east", {title: "East"}));
32812
32813 reader.regions.listView.add(new CP("listView", "List"));
32814 reader.regions.preview.add(new CP("preview", "Preview"));
32815 reader.endUpdate();
32816 </code></pre>
32817 * @constructor
32818 * Create a new ReaderLayout
32819 * @param {Object} config Configuration options
32820 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32821 * document.body if omitted)
32822 */
32823 Roo.ReaderLayout = function(config, renderTo){
32824     var c = config || {size:{}};
32825     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32826         north: c.north !== false ? Roo.apply({
32827             split:false,
32828             initialSize: 32,
32829             titlebar: false
32830         }, c.north) : false,
32831         west: c.west !== false ? Roo.apply({
32832             split:true,
32833             initialSize: 200,
32834             minSize: 175,
32835             maxSize: 400,
32836             titlebar: true,
32837             collapsible: true,
32838             animate: true,
32839             margins:{left:5,right:0,bottom:5,top:5},
32840             cmargins:{left:5,right:5,bottom:5,top:5}
32841         }, c.west) : false,
32842         east: c.east !== false ? Roo.apply({
32843             split:true,
32844             initialSize: 200,
32845             minSize: 175,
32846             maxSize: 400,
32847             titlebar: true,
32848             collapsible: true,
32849             animate: true,
32850             margins:{left:0,right:5,bottom:5,top:5},
32851             cmargins:{left:5,right:5,bottom:5,top:5}
32852         }, c.east) : false,
32853         center: Roo.apply({
32854             tabPosition: 'top',
32855             autoScroll:false,
32856             closeOnTab: true,
32857             titlebar:false,
32858             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32859         }, c.center)
32860     });
32861
32862     this.el.addClass('x-reader');
32863
32864     this.beginUpdate();
32865
32866     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32867         south: c.preview !== false ? Roo.apply({
32868             split:true,
32869             initialSize: 200,
32870             minSize: 100,
32871             autoScroll:true,
32872             collapsible:true,
32873             titlebar: true,
32874             cmargins:{top:5,left:0, right:0, bottom:0}
32875         }, c.preview) : false,
32876         center: Roo.apply({
32877             autoScroll:false,
32878             titlebar:false,
32879             minHeight:200
32880         }, c.listView)
32881     });
32882     this.add('center', new Roo.NestedLayoutPanel(inner,
32883             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32884
32885     this.endUpdate();
32886
32887     this.regions.preview = inner.getRegion('south');
32888     this.regions.listView = inner.getRegion('center');
32889 };
32890
32891 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32892  * Based on:
32893  * Ext JS Library 1.1.1
32894  * Copyright(c) 2006-2007, Ext JS, LLC.
32895  *
32896  * Originally Released Under LGPL - original licence link has changed is not relivant.
32897  *
32898  * Fork - LGPL
32899  * <script type="text/javascript">
32900  */
32901  
32902 /**
32903  * @class Roo.grid.Grid
32904  * @extends Roo.util.Observable
32905  * This class represents the primary interface of a component based grid control.
32906  * <br><br>Usage:<pre><code>
32907  var grid = new Roo.grid.Grid("my-container-id", {
32908      ds: myDataStore,
32909      cm: myColModel,
32910      selModel: mySelectionModel,
32911      autoSizeColumns: true,
32912      monitorWindowResize: false,
32913      trackMouseOver: true
32914  });
32915  // set any options
32916  grid.render();
32917  * </code></pre>
32918  * <b>Common Problems:</b><br/>
32919  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32920  * element will correct this<br/>
32921  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32922  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32923  * are unpredictable.<br/>
32924  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32925  * grid to calculate dimensions/offsets.<br/>
32926   * @constructor
32927  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32928  * The container MUST have some type of size defined for the grid to fill. The container will be
32929  * automatically set to position relative if it isn't already.
32930  * @param {Object} config A config object that sets properties on this grid.
32931  */
32932 Roo.grid.Grid = function(container, config){
32933         // initialize the container
32934         this.container = Roo.get(container);
32935         this.container.update("");
32936         this.container.setStyle("overflow", "hidden");
32937     this.container.addClass('x-grid-container');
32938
32939     this.id = this.container.id;
32940
32941     Roo.apply(this, config);
32942     // check and correct shorthanded configs
32943     if(this.ds){
32944         this.dataSource = this.ds;
32945         delete this.ds;
32946     }
32947     if(this.cm){
32948         this.colModel = this.cm;
32949         delete this.cm;
32950     }
32951     if(this.sm){
32952         this.selModel = this.sm;
32953         delete this.sm;
32954     }
32955
32956     if (this.selModel) {
32957         this.selModel = Roo.factory(this.selModel, Roo.grid);
32958         this.sm = this.selModel;
32959         this.sm.xmodule = this.xmodule || false;
32960     }
32961     if (typeof(this.colModel.config) == 'undefined') {
32962         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32963         this.cm = this.colModel;
32964         this.cm.xmodule = this.xmodule || false;
32965     }
32966     if (this.dataSource) {
32967         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32968         this.ds = this.dataSource;
32969         this.ds.xmodule = this.xmodule || false;
32970          
32971     }
32972     
32973     
32974     
32975     if(this.width){
32976         this.container.setWidth(this.width);
32977     }
32978
32979     if(this.height){
32980         this.container.setHeight(this.height);
32981     }
32982     /** @private */
32983         this.addEvents({
32984         // raw events
32985         /**
32986          * @event click
32987          * The raw click event for the entire grid.
32988          * @param {Roo.EventObject} e
32989          */
32990         "click" : true,
32991         /**
32992          * @event dblclick
32993          * The raw dblclick event for the entire grid.
32994          * @param {Roo.EventObject} e
32995          */
32996         "dblclick" : true,
32997         /**
32998          * @event contextmenu
32999          * The raw contextmenu event for the entire grid.
33000          * @param {Roo.EventObject} e
33001          */
33002         "contextmenu" : true,
33003         /**
33004          * @event mousedown
33005          * The raw mousedown event for the entire grid.
33006          * @param {Roo.EventObject} e
33007          */
33008         "mousedown" : true,
33009         /**
33010          * @event mouseup
33011          * The raw mouseup event for the entire grid.
33012          * @param {Roo.EventObject} e
33013          */
33014         "mouseup" : true,
33015         /**
33016          * @event mouseover
33017          * The raw mouseover event for the entire grid.
33018          * @param {Roo.EventObject} e
33019          */
33020         "mouseover" : true,
33021         /**
33022          * @event mouseout
33023          * The raw mouseout event for the entire grid.
33024          * @param {Roo.EventObject} e
33025          */
33026         "mouseout" : true,
33027         /**
33028          * @event keypress
33029          * The raw keypress event for the entire grid.
33030          * @param {Roo.EventObject} e
33031          */
33032         "keypress" : true,
33033         /**
33034          * @event keydown
33035          * The raw keydown event for the entire grid.
33036          * @param {Roo.EventObject} e
33037          */
33038         "keydown" : true,
33039
33040         // custom events
33041
33042         /**
33043          * @event cellclick
33044          * Fires when a cell is clicked
33045          * @param {Grid} this
33046          * @param {Number} rowIndex
33047          * @param {Number} columnIndex
33048          * @param {Roo.EventObject} e
33049          */
33050         "cellclick" : true,
33051         /**
33052          * @event celldblclick
33053          * Fires when a cell is double clicked
33054          * @param {Grid} this
33055          * @param {Number} rowIndex
33056          * @param {Number} columnIndex
33057          * @param {Roo.EventObject} e
33058          */
33059         "celldblclick" : true,
33060         /**
33061          * @event rowclick
33062          * Fires when a row is clicked
33063          * @param {Grid} this
33064          * @param {Number} rowIndex
33065          * @param {Roo.EventObject} e
33066          */
33067         "rowclick" : true,
33068         /**
33069          * @event rowdblclick
33070          * Fires when a row is double clicked
33071          * @param {Grid} this
33072          * @param {Number} rowIndex
33073          * @param {Roo.EventObject} e
33074          */
33075         "rowdblclick" : true,
33076         /**
33077          * @event headerclick
33078          * Fires when a header is clicked
33079          * @param {Grid} this
33080          * @param {Number} columnIndex
33081          * @param {Roo.EventObject} e
33082          */
33083         "headerclick" : true,
33084         /**
33085          * @event headerdblclick
33086          * Fires when a header cell is double clicked
33087          * @param {Grid} this
33088          * @param {Number} columnIndex
33089          * @param {Roo.EventObject} e
33090          */
33091         "headerdblclick" : true,
33092         /**
33093          * @event rowcontextmenu
33094          * Fires when a row is right clicked
33095          * @param {Grid} this
33096          * @param {Number} rowIndex
33097          * @param {Roo.EventObject} e
33098          */
33099         "rowcontextmenu" : true,
33100         /**
33101          * @event cellcontextmenu
33102          * Fires when a cell is right clicked
33103          * @param {Grid} this
33104          * @param {Number} rowIndex
33105          * @param {Number} cellIndex
33106          * @param {Roo.EventObject} e
33107          */
33108          "cellcontextmenu" : true,
33109         /**
33110          * @event headercontextmenu
33111          * Fires when a header is right clicked
33112          * @param {Grid} this
33113          * @param {Number} columnIndex
33114          * @param {Roo.EventObject} e
33115          */
33116         "headercontextmenu" : true,
33117         /**
33118          * @event bodyscroll
33119          * Fires when the body element is scrolled
33120          * @param {Number} scrollLeft
33121          * @param {Number} scrollTop
33122          */
33123         "bodyscroll" : true,
33124         /**
33125          * @event columnresize
33126          * Fires when the user resizes a column
33127          * @param {Number} columnIndex
33128          * @param {Number} newSize
33129          */
33130         "columnresize" : true,
33131         /**
33132          * @event columnmove
33133          * Fires when the user moves a column
33134          * @param {Number} oldIndex
33135          * @param {Number} newIndex
33136          */
33137         "columnmove" : true,
33138         /**
33139          * @event startdrag
33140          * Fires when row(s) start being dragged
33141          * @param {Grid} this
33142          * @param {Roo.GridDD} dd The drag drop object
33143          * @param {event} e The raw browser event
33144          */
33145         "startdrag" : true,
33146         /**
33147          * @event enddrag
33148          * Fires when a drag operation is complete
33149          * @param {Grid} this
33150          * @param {Roo.GridDD} dd The drag drop object
33151          * @param {event} e The raw browser event
33152          */
33153         "enddrag" : true,
33154         /**
33155          * @event dragdrop
33156          * Fires when dragged row(s) are dropped on a valid DD target
33157          * @param {Grid} this
33158          * @param {Roo.GridDD} dd The drag drop object
33159          * @param {String} targetId The target drag drop object
33160          * @param {event} e The raw browser event
33161          */
33162         "dragdrop" : true,
33163         /**
33164          * @event dragover
33165          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33166          * @param {Grid} this
33167          * @param {Roo.GridDD} dd The drag drop object
33168          * @param {String} targetId The target drag drop object
33169          * @param {event} e The raw browser event
33170          */
33171         "dragover" : true,
33172         /**
33173          * @event dragenter
33174          *  Fires when the dragged row(s) first cross another DD target while being dragged
33175          * @param {Grid} this
33176          * @param {Roo.GridDD} dd The drag drop object
33177          * @param {String} targetId The target drag drop object
33178          * @param {event} e The raw browser event
33179          */
33180         "dragenter" : true,
33181         /**
33182          * @event dragout
33183          * Fires when the dragged row(s) leave another DD target while being dragged
33184          * @param {Grid} this
33185          * @param {Roo.GridDD} dd The drag drop object
33186          * @param {String} targetId The target drag drop object
33187          * @param {event} e The raw browser event
33188          */
33189         "dragout" : true,
33190         /**
33191          * @event rowclass
33192          * Fires when a row is rendered, so you can change add a style to it.
33193          * @param {GridView} gridview   The grid view
33194          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33195          */
33196         'rowclass' : true,
33197
33198         /**
33199          * @event render
33200          * Fires when the grid is rendered
33201          * @param {Grid} grid
33202          */
33203         'render' : true
33204     });
33205
33206     Roo.grid.Grid.superclass.constructor.call(this);
33207 };
33208 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33209     
33210     /**
33211      * @cfg {String} ddGroup - drag drop group.
33212      */
33213
33214     /**
33215      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33216      */
33217     minColumnWidth : 25,
33218
33219     /**
33220      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33221      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33222      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33223      */
33224     autoSizeColumns : false,
33225
33226     /**
33227      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33228      */
33229     autoSizeHeaders : true,
33230
33231     /**
33232      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33233      */
33234     monitorWindowResize : true,
33235
33236     /**
33237      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33238      * rows measured to get a columns size. Default is 0 (all rows).
33239      */
33240     maxRowsToMeasure : 0,
33241
33242     /**
33243      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33244      */
33245     trackMouseOver : true,
33246
33247     /**
33248     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33249     */
33250     
33251     /**
33252     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33253     */
33254     enableDragDrop : false,
33255     
33256     /**
33257     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33258     */
33259     enableColumnMove : true,
33260     
33261     /**
33262     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33263     */
33264     enableColumnHide : true,
33265     
33266     /**
33267     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33268     */
33269     enableRowHeightSync : false,
33270     
33271     /**
33272     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33273     */
33274     stripeRows : true,
33275     
33276     /**
33277     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33278     */
33279     autoHeight : false,
33280
33281     /**
33282      * @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.
33283      */
33284     autoExpandColumn : false,
33285
33286     /**
33287     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33288     * Default is 50.
33289     */
33290     autoExpandMin : 50,
33291
33292     /**
33293     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
33294     */
33295     autoExpandMax : 1000,
33296
33297     /**
33298     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
33299     */
33300     view : null,
33301
33302     /**
33303     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
33304     */
33305     loadMask : false,
33306     /**
33307     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
33308     */
33309     dropTarget: false,
33310     
33311    
33312     
33313     // private
33314     rendered : false,
33315
33316     /**
33317     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
33318     * of a fixed width. Default is false.
33319     */
33320     /**
33321     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
33322     */
33323     /**
33324      * Called once after all setup has been completed and the grid is ready to be rendered.
33325      * @return {Roo.grid.Grid} this
33326      */
33327     render : function()
33328     {
33329         var c = this.container;
33330         // try to detect autoHeight/width mode
33331         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
33332             this.autoHeight = true;
33333         }
33334         var view = this.getView();
33335         view.init(this);
33336
33337         c.on("click", this.onClick, this);
33338         c.on("dblclick", this.onDblClick, this);
33339         c.on("contextmenu", this.onContextMenu, this);
33340         c.on("keydown", this.onKeyDown, this);
33341
33342         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
33343
33344         this.getSelectionModel().init(this);
33345
33346         view.render();
33347
33348         if(this.loadMask){
33349             this.loadMask = new Roo.LoadMask(this.container,
33350                     Roo.apply({store:this.dataSource}, this.loadMask));
33351         }
33352         
33353         
33354         if (this.toolbar && this.toolbar.xtype) {
33355             this.toolbar.container = this.getView().getHeaderPanel(true);
33356             this.toolbar = new Roo.Toolbar(this.toolbar);
33357         }
33358         if (this.footer && this.footer.xtype) {
33359             this.footer.dataSource = this.getDataSource();
33360             this.footer.container = this.getView().getFooterPanel(true);
33361             this.footer = Roo.factory(this.footer, Roo);
33362         }
33363         if (this.dropTarget && this.dropTarget.xtype) {
33364             delete this.dropTarget.xtype;
33365             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
33366         }
33367         
33368         
33369         this.rendered = true;
33370         this.fireEvent('render', this);
33371         return this;
33372     },
33373
33374         /**
33375          * Reconfigures the grid to use a different Store and Column Model.
33376          * The View will be bound to the new objects and refreshed.
33377          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
33378          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
33379          */
33380     reconfigure : function(dataSource, colModel){
33381         if(this.loadMask){
33382             this.loadMask.destroy();
33383             this.loadMask = new Roo.LoadMask(this.container,
33384                     Roo.apply({store:dataSource}, this.loadMask));
33385         }
33386         this.view.bind(dataSource, colModel);
33387         this.dataSource = dataSource;
33388         this.colModel = colModel;
33389         this.view.refresh(true);
33390     },
33391
33392     // private
33393     onKeyDown : function(e){
33394         this.fireEvent("keydown", e);
33395     },
33396
33397     /**
33398      * Destroy this grid.
33399      * @param {Boolean} removeEl True to remove the element
33400      */
33401     destroy : function(removeEl, keepListeners){
33402         if(this.loadMask){
33403             this.loadMask.destroy();
33404         }
33405         var c = this.container;
33406         c.removeAllListeners();
33407         this.view.destroy();
33408         this.colModel.purgeListeners();
33409         if(!keepListeners){
33410             this.purgeListeners();
33411         }
33412         c.update("");
33413         if(removeEl === true){
33414             c.remove();
33415         }
33416     },
33417
33418     // private
33419     processEvent : function(name, e){
33420         this.fireEvent(name, e);
33421         var t = e.getTarget();
33422         var v = this.view;
33423         var header = v.findHeaderIndex(t);
33424         if(header !== false){
33425             this.fireEvent("header" + name, this, header, e);
33426         }else{
33427             var row = v.findRowIndex(t);
33428             var cell = v.findCellIndex(t);
33429             if(row !== false){
33430                 this.fireEvent("row" + name, this, row, e);
33431                 if(cell !== false){
33432                     this.fireEvent("cell" + name, this, row, cell, e);
33433                 }
33434             }
33435         }
33436     },
33437
33438     // private
33439     onClick : function(e){
33440         this.processEvent("click", e);
33441     },
33442
33443     // private
33444     onContextMenu : function(e, t){
33445         this.processEvent("contextmenu", e);
33446     },
33447
33448     // private
33449     onDblClick : function(e){
33450         this.processEvent("dblclick", e);
33451     },
33452
33453     // private
33454     walkCells : function(row, col, step, fn, scope){
33455         var cm = this.colModel, clen = cm.getColumnCount();
33456         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33457         if(step < 0){
33458             if(col < 0){
33459                 row--;
33460                 first = false;
33461             }
33462             while(row >= 0){
33463                 if(!first){
33464                     col = clen-1;
33465                 }
33466                 first = false;
33467                 while(col >= 0){
33468                     if(fn.call(scope || this, row, col, cm) === true){
33469                         return [row, col];
33470                     }
33471                     col--;
33472                 }
33473                 row--;
33474             }
33475         } else {
33476             if(col >= clen){
33477                 row++;
33478                 first = false;
33479             }
33480             while(row < rlen){
33481                 if(!first){
33482                     col = 0;
33483                 }
33484                 first = false;
33485                 while(col < clen){
33486                     if(fn.call(scope || this, row, col, cm) === true){
33487                         return [row, col];
33488                     }
33489                     col++;
33490                 }
33491                 row++;
33492             }
33493         }
33494         return null;
33495     },
33496
33497     // private
33498     getSelections : function(){
33499         return this.selModel.getSelections();
33500     },
33501
33502     /**
33503      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33504      * but if manual update is required this method will initiate it.
33505      */
33506     autoSize : function(){
33507         if(this.rendered){
33508             this.view.layout();
33509             if(this.view.adjustForScroll){
33510                 this.view.adjustForScroll();
33511             }
33512         }
33513     },
33514
33515     /**
33516      * Returns the grid's underlying element.
33517      * @return {Element} The element
33518      */
33519     getGridEl : function(){
33520         return this.container;
33521     },
33522
33523     // private for compatibility, overridden by editor grid
33524     stopEditing : function(){},
33525
33526     /**
33527      * Returns the grid's SelectionModel.
33528      * @return {SelectionModel}
33529      */
33530     getSelectionModel : function(){
33531         if(!this.selModel){
33532             this.selModel = new Roo.grid.RowSelectionModel();
33533         }
33534         return this.selModel;
33535     },
33536
33537     /**
33538      * Returns the grid's DataSource.
33539      * @return {DataSource}
33540      */
33541     getDataSource : function(){
33542         return this.dataSource;
33543     },
33544
33545     /**
33546      * Returns the grid's ColumnModel.
33547      * @return {ColumnModel}
33548      */
33549     getColumnModel : function(){
33550         return this.colModel;
33551     },
33552
33553     /**
33554      * Returns the grid's GridView object.
33555      * @return {GridView}
33556      */
33557     getView : function(){
33558         if(!this.view){
33559             this.view = new Roo.grid.GridView(this.viewConfig);
33560         }
33561         return this.view;
33562     },
33563     /**
33564      * Called to get grid's drag proxy text, by default returns this.ddText.
33565      * @return {String}
33566      */
33567     getDragDropText : function(){
33568         var count = this.selModel.getCount();
33569         return String.format(this.ddText, count, count == 1 ? '' : 's');
33570     }
33571 });
33572 /**
33573  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33574  * %0 is replaced with the number of selected rows.
33575  * @type String
33576  */
33577 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33578  * Based on:
33579  * Ext JS Library 1.1.1
33580  * Copyright(c) 2006-2007, Ext JS, LLC.
33581  *
33582  * Originally Released Under LGPL - original licence link has changed is not relivant.
33583  *
33584  * Fork - LGPL
33585  * <script type="text/javascript">
33586  */
33587  
33588 Roo.grid.AbstractGridView = function(){
33589         this.grid = null;
33590         
33591         this.events = {
33592             "beforerowremoved" : true,
33593             "beforerowsinserted" : true,
33594             "beforerefresh" : true,
33595             "rowremoved" : true,
33596             "rowsinserted" : true,
33597             "rowupdated" : true,
33598             "refresh" : true
33599         };
33600     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33601 };
33602
33603 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33604     rowClass : "x-grid-row",
33605     cellClass : "x-grid-cell",
33606     tdClass : "x-grid-td",
33607     hdClass : "x-grid-hd",
33608     splitClass : "x-grid-hd-split",
33609     
33610         init: function(grid){
33611         this.grid = grid;
33612                 var cid = this.grid.getGridEl().id;
33613         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33614         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33615         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33616         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33617         },
33618         
33619         getColumnRenderers : function(){
33620         var renderers = [];
33621         var cm = this.grid.colModel;
33622         var colCount = cm.getColumnCount();
33623         for(var i = 0; i < colCount; i++){
33624             renderers[i] = cm.getRenderer(i);
33625         }
33626         return renderers;
33627     },
33628     
33629     getColumnIds : function(){
33630         var ids = [];
33631         var cm = this.grid.colModel;
33632         var colCount = cm.getColumnCount();
33633         for(var i = 0; i < colCount; i++){
33634             ids[i] = cm.getColumnId(i);
33635         }
33636         return ids;
33637     },
33638     
33639     getDataIndexes : function(){
33640         if(!this.indexMap){
33641             this.indexMap = this.buildIndexMap();
33642         }
33643         return this.indexMap.colToData;
33644     },
33645     
33646     getColumnIndexByDataIndex : function(dataIndex){
33647         if(!this.indexMap){
33648             this.indexMap = this.buildIndexMap();
33649         }
33650         return this.indexMap.dataToCol[dataIndex];
33651     },
33652     
33653     /**
33654      * Set a css style for a column dynamically. 
33655      * @param {Number} colIndex The index of the column
33656      * @param {String} name The css property name
33657      * @param {String} value The css value
33658      */
33659     setCSSStyle : function(colIndex, name, value){
33660         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33661         Roo.util.CSS.updateRule(selector, name, value);
33662     },
33663     
33664     generateRules : function(cm){
33665         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33666         Roo.util.CSS.removeStyleSheet(rulesId);
33667         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33668             var cid = cm.getColumnId(i);
33669             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33670                          this.tdSelector, cid, " {\n}\n",
33671                          this.hdSelector, cid, " {\n}\n",
33672                          this.splitSelector, cid, " {\n}\n");
33673         }
33674         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33675     }
33676 });/*
33677  * Based on:
33678  * Ext JS Library 1.1.1
33679  * Copyright(c) 2006-2007, Ext JS, LLC.
33680  *
33681  * Originally Released Under LGPL - original licence link has changed is not relivant.
33682  *
33683  * Fork - LGPL
33684  * <script type="text/javascript">
33685  */
33686
33687 // private
33688 // This is a support class used internally by the Grid components
33689 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33690     this.grid = grid;
33691     this.view = grid.getView();
33692     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33693     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33694     if(hd2){
33695         this.setHandleElId(Roo.id(hd));
33696         this.setOuterHandleElId(Roo.id(hd2));
33697     }
33698     this.scroll = false;
33699 };
33700 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33701     maxDragWidth: 120,
33702     getDragData : function(e){
33703         var t = Roo.lib.Event.getTarget(e);
33704         var h = this.view.findHeaderCell(t);
33705         if(h){
33706             return {ddel: h.firstChild, header:h};
33707         }
33708         return false;
33709     },
33710
33711     onInitDrag : function(e){
33712         this.view.headersDisabled = true;
33713         var clone = this.dragData.ddel.cloneNode(true);
33714         clone.id = Roo.id();
33715         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33716         this.proxy.update(clone);
33717         return true;
33718     },
33719
33720     afterValidDrop : function(){
33721         var v = this.view;
33722         setTimeout(function(){
33723             v.headersDisabled = false;
33724         }, 50);
33725     },
33726
33727     afterInvalidDrop : function(){
33728         var v = this.view;
33729         setTimeout(function(){
33730             v.headersDisabled = false;
33731         }, 50);
33732     }
33733 });
33734 /*
33735  * Based on:
33736  * Ext JS Library 1.1.1
33737  * Copyright(c) 2006-2007, Ext JS, LLC.
33738  *
33739  * Originally Released Under LGPL - original licence link has changed is not relivant.
33740  *
33741  * Fork - LGPL
33742  * <script type="text/javascript">
33743  */
33744 // private
33745 // This is a support class used internally by the Grid components
33746 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33747     this.grid = grid;
33748     this.view = grid.getView();
33749     // split the proxies so they don't interfere with mouse events
33750     this.proxyTop = Roo.DomHelper.append(document.body, {
33751         cls:"col-move-top", html:"&#160;"
33752     }, true);
33753     this.proxyBottom = Roo.DomHelper.append(document.body, {
33754         cls:"col-move-bottom", html:"&#160;"
33755     }, true);
33756     this.proxyTop.hide = this.proxyBottom.hide = function(){
33757         this.setLeftTop(-100,-100);
33758         this.setStyle("visibility", "hidden");
33759     };
33760     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33761     // temporarily disabled
33762     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33763     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33764 };
33765 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33766     proxyOffsets : [-4, -9],
33767     fly: Roo.Element.fly,
33768
33769     getTargetFromEvent : function(e){
33770         var t = Roo.lib.Event.getTarget(e);
33771         var cindex = this.view.findCellIndex(t);
33772         if(cindex !== false){
33773             return this.view.getHeaderCell(cindex);
33774         }
33775         return null;
33776     },
33777
33778     nextVisible : function(h){
33779         var v = this.view, cm = this.grid.colModel;
33780         h = h.nextSibling;
33781         while(h){
33782             if(!cm.isHidden(v.getCellIndex(h))){
33783                 return h;
33784             }
33785             h = h.nextSibling;
33786         }
33787         return null;
33788     },
33789
33790     prevVisible : function(h){
33791         var v = this.view, cm = this.grid.colModel;
33792         h = h.prevSibling;
33793         while(h){
33794             if(!cm.isHidden(v.getCellIndex(h))){
33795                 return h;
33796             }
33797             h = h.prevSibling;
33798         }
33799         return null;
33800     },
33801
33802     positionIndicator : function(h, n, e){
33803         var x = Roo.lib.Event.getPageX(e);
33804         var r = Roo.lib.Dom.getRegion(n.firstChild);
33805         var px, pt, py = r.top + this.proxyOffsets[1];
33806         if((r.right - x) <= (r.right-r.left)/2){
33807             px = r.right+this.view.borderWidth;
33808             pt = "after";
33809         }else{
33810             px = r.left;
33811             pt = "before";
33812         }
33813         var oldIndex = this.view.getCellIndex(h);
33814         var newIndex = this.view.getCellIndex(n);
33815
33816         if(this.grid.colModel.isFixed(newIndex)){
33817             return false;
33818         }
33819
33820         var locked = this.grid.colModel.isLocked(newIndex);
33821
33822         if(pt == "after"){
33823             newIndex++;
33824         }
33825         if(oldIndex < newIndex){
33826             newIndex--;
33827         }
33828         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33829             return false;
33830         }
33831         px +=  this.proxyOffsets[0];
33832         this.proxyTop.setLeftTop(px, py);
33833         this.proxyTop.show();
33834         if(!this.bottomOffset){
33835             this.bottomOffset = this.view.mainHd.getHeight();
33836         }
33837         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33838         this.proxyBottom.show();
33839         return pt;
33840     },
33841
33842     onNodeEnter : function(n, dd, e, data){
33843         if(data.header != n){
33844             this.positionIndicator(data.header, n, e);
33845         }
33846     },
33847
33848     onNodeOver : function(n, dd, e, data){
33849         var result = false;
33850         if(data.header != n){
33851             result = this.positionIndicator(data.header, n, e);
33852         }
33853         if(!result){
33854             this.proxyTop.hide();
33855             this.proxyBottom.hide();
33856         }
33857         return result ? this.dropAllowed : this.dropNotAllowed;
33858     },
33859
33860     onNodeOut : function(n, dd, e, data){
33861         this.proxyTop.hide();
33862         this.proxyBottom.hide();
33863     },
33864
33865     onNodeDrop : function(n, dd, e, data){
33866         var h = data.header;
33867         if(h != n){
33868             var cm = this.grid.colModel;
33869             var x = Roo.lib.Event.getPageX(e);
33870             var r = Roo.lib.Dom.getRegion(n.firstChild);
33871             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33872             var oldIndex = this.view.getCellIndex(h);
33873             var newIndex = this.view.getCellIndex(n);
33874             var locked = cm.isLocked(newIndex);
33875             if(pt == "after"){
33876                 newIndex++;
33877             }
33878             if(oldIndex < newIndex){
33879                 newIndex--;
33880             }
33881             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33882                 return false;
33883             }
33884             cm.setLocked(oldIndex, locked, true);
33885             cm.moveColumn(oldIndex, newIndex);
33886             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33887             return true;
33888         }
33889         return false;
33890     }
33891 });
33892 /*
33893  * Based on:
33894  * Ext JS Library 1.1.1
33895  * Copyright(c) 2006-2007, Ext JS, LLC.
33896  *
33897  * Originally Released Under LGPL - original licence link has changed is not relivant.
33898  *
33899  * Fork - LGPL
33900  * <script type="text/javascript">
33901  */
33902   
33903 /**
33904  * @class Roo.grid.GridView
33905  * @extends Roo.util.Observable
33906  *
33907  * @constructor
33908  * @param {Object} config
33909  */
33910 Roo.grid.GridView = function(config){
33911     Roo.grid.GridView.superclass.constructor.call(this);
33912     this.el = null;
33913
33914     Roo.apply(this, config);
33915 };
33916
33917 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33918
33919     /**
33920      * Override this function to apply custom css classes to rows during rendering
33921      * @param {Record} record The record
33922      * @param {Number} index
33923      * @method getRowClass
33924      */
33925     rowClass : "x-grid-row",
33926
33927     cellClass : "x-grid-col",
33928
33929     tdClass : "x-grid-td",
33930
33931     hdClass : "x-grid-hd",
33932
33933     splitClass : "x-grid-split",
33934
33935     sortClasses : ["sort-asc", "sort-desc"],
33936
33937     enableMoveAnim : false,
33938
33939     hlColor: "C3DAF9",
33940
33941     dh : Roo.DomHelper,
33942
33943     fly : Roo.Element.fly,
33944
33945     css : Roo.util.CSS,
33946
33947     borderWidth: 1,
33948
33949     splitOffset: 3,
33950
33951     scrollIncrement : 22,
33952
33953     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33954
33955     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33956
33957     bind : function(ds, cm){
33958         if(this.ds){
33959             this.ds.un("load", this.onLoad, this);
33960             this.ds.un("datachanged", this.onDataChange, this);
33961             this.ds.un("add", this.onAdd, this);
33962             this.ds.un("remove", this.onRemove, this);
33963             this.ds.un("update", this.onUpdate, this);
33964             this.ds.un("clear", this.onClear, this);
33965         }
33966         if(ds){
33967             ds.on("load", this.onLoad, this);
33968             ds.on("datachanged", this.onDataChange, this);
33969             ds.on("add", this.onAdd, this);
33970             ds.on("remove", this.onRemove, this);
33971             ds.on("update", this.onUpdate, this);
33972             ds.on("clear", this.onClear, this);
33973         }
33974         this.ds = ds;
33975
33976         if(this.cm){
33977             this.cm.un("widthchange", this.onColWidthChange, this);
33978             this.cm.un("headerchange", this.onHeaderChange, this);
33979             this.cm.un("hiddenchange", this.onHiddenChange, this);
33980             this.cm.un("columnmoved", this.onColumnMove, this);
33981             this.cm.un("columnlockchange", this.onColumnLock, this);
33982         }
33983         if(cm){
33984             this.generateRules(cm);
33985             cm.on("widthchange", this.onColWidthChange, this);
33986             cm.on("headerchange", this.onHeaderChange, this);
33987             cm.on("hiddenchange", this.onHiddenChange, this);
33988             cm.on("columnmoved", this.onColumnMove, this);
33989             cm.on("columnlockchange", this.onColumnLock, this);
33990         }
33991         this.cm = cm;
33992     },
33993
33994     init: function(grid){
33995         Roo.grid.GridView.superclass.init.call(this, grid);
33996
33997         this.bind(grid.dataSource, grid.colModel);
33998
33999         grid.on("headerclick", this.handleHeaderClick, this);
34000
34001         if(grid.trackMouseOver){
34002             grid.on("mouseover", this.onRowOver, this);
34003             grid.on("mouseout", this.onRowOut, this);
34004         }
34005         grid.cancelTextSelection = function(){};
34006         this.gridId = grid.id;
34007
34008         var tpls = this.templates || {};
34009
34010         if(!tpls.master){
34011             tpls.master = new Roo.Template(
34012                '<div class="x-grid" hidefocus="true">',
34013                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34014                   '<div class="x-grid-topbar"></div>',
34015                   '<div class="x-grid-scroller"><div></div></div>',
34016                   '<div class="x-grid-locked">',
34017                       '<div class="x-grid-header">{lockedHeader}</div>',
34018                       '<div class="x-grid-body">{lockedBody}</div>',
34019                   "</div>",
34020                   '<div class="x-grid-viewport">',
34021                       '<div class="x-grid-header">{header}</div>',
34022                       '<div class="x-grid-body">{body}</div>',
34023                   "</div>",
34024                   '<div class="x-grid-bottombar"></div>',
34025                  
34026                   '<div class="x-grid-resize-proxy">&#160;</div>',
34027                "</div>"
34028             );
34029             tpls.master.disableformats = true;
34030         }
34031
34032         if(!tpls.header){
34033             tpls.header = new Roo.Template(
34034                '<table border="0" cellspacing="0" cellpadding="0">',
34035                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34036                "</table>{splits}"
34037             );
34038             tpls.header.disableformats = true;
34039         }
34040         tpls.header.compile();
34041
34042         if(!tpls.hcell){
34043             tpls.hcell = new Roo.Template(
34044                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34045                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34046                 "</div></td>"
34047              );
34048              tpls.hcell.disableFormats = true;
34049         }
34050         tpls.hcell.compile();
34051
34052         if(!tpls.hsplit){
34053             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34054             tpls.hsplit.disableFormats = true;
34055         }
34056         tpls.hsplit.compile();
34057
34058         if(!tpls.body){
34059             tpls.body = new Roo.Template(
34060                '<table border="0" cellspacing="0" cellpadding="0">',
34061                "<tbody>{rows}</tbody>",
34062                "</table>"
34063             );
34064             tpls.body.disableFormats = true;
34065         }
34066         tpls.body.compile();
34067
34068         if(!tpls.row){
34069             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34070             tpls.row.disableFormats = true;
34071         }
34072         tpls.row.compile();
34073
34074         if(!tpls.cell){
34075             tpls.cell = new Roo.Template(
34076                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34077                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34078                 "</td>"
34079             );
34080             tpls.cell.disableFormats = true;
34081         }
34082         tpls.cell.compile();
34083
34084         this.templates = tpls;
34085     },
34086
34087     // remap these for backwards compat
34088     onColWidthChange : function(){
34089         this.updateColumns.apply(this, arguments);
34090     },
34091     onHeaderChange : function(){
34092         this.updateHeaders.apply(this, arguments);
34093     }, 
34094     onHiddenChange : function(){
34095         this.handleHiddenChange.apply(this, arguments);
34096     },
34097     onColumnMove : function(){
34098         this.handleColumnMove.apply(this, arguments);
34099     },
34100     onColumnLock : function(){
34101         this.handleLockChange.apply(this, arguments);
34102     },
34103
34104     onDataChange : function(){
34105         this.refresh();
34106         this.updateHeaderSortState();
34107     },
34108
34109     onClear : function(){
34110         this.refresh();
34111     },
34112
34113     onUpdate : function(ds, record){
34114         this.refreshRow(record);
34115     },
34116
34117     refreshRow : function(record){
34118         var ds = this.ds, index;
34119         if(typeof record == 'number'){
34120             index = record;
34121             record = ds.getAt(index);
34122         }else{
34123             index = ds.indexOf(record);
34124         }
34125         this.insertRows(ds, index, index, true);
34126         this.onRemove(ds, record, index+1, true);
34127         this.syncRowHeights(index, index);
34128         this.layout();
34129         this.fireEvent("rowupdated", this, index, record);
34130     },
34131
34132     onAdd : function(ds, records, index){
34133         this.insertRows(ds, index, index + (records.length-1));
34134     },
34135
34136     onRemove : function(ds, record, index, isUpdate){
34137         if(isUpdate !== true){
34138             this.fireEvent("beforerowremoved", this, index, record);
34139         }
34140         var bt = this.getBodyTable(), lt = this.getLockedTable();
34141         if(bt.rows[index]){
34142             bt.firstChild.removeChild(bt.rows[index]);
34143         }
34144         if(lt.rows[index]){
34145             lt.firstChild.removeChild(lt.rows[index]);
34146         }
34147         if(isUpdate !== true){
34148             this.stripeRows(index);
34149             this.syncRowHeights(index, index);
34150             this.layout();
34151             this.fireEvent("rowremoved", this, index, record);
34152         }
34153     },
34154
34155     onLoad : function(){
34156         this.scrollToTop();
34157     },
34158
34159     /**
34160      * Scrolls the grid to the top
34161      */
34162     scrollToTop : function(){
34163         if(this.scroller){
34164             this.scroller.dom.scrollTop = 0;
34165             this.syncScroll();
34166         }
34167     },
34168
34169     /**
34170      * Gets a panel in the header of the grid that can be used for toolbars etc.
34171      * After modifying the contents of this panel a call to grid.autoSize() may be
34172      * required to register any changes in size.
34173      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34174      * @return Roo.Element
34175      */
34176     getHeaderPanel : function(doShow){
34177         if(doShow){
34178             this.headerPanel.show();
34179         }
34180         return this.headerPanel;
34181     },
34182
34183     /**
34184      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34185      * After modifying the contents of this panel a call to grid.autoSize() may be
34186      * required to register any changes in size.
34187      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34188      * @return Roo.Element
34189      */
34190     getFooterPanel : function(doShow){
34191         if(doShow){
34192             this.footerPanel.show();
34193         }
34194         return this.footerPanel;
34195     },
34196
34197     initElements : function(){
34198         var E = Roo.Element;
34199         var el = this.grid.getGridEl().dom.firstChild;
34200         var cs = el.childNodes;
34201
34202         this.el = new E(el);
34203         
34204          this.focusEl = new E(el.firstChild);
34205         this.focusEl.swallowEvent("click", true);
34206         
34207         this.headerPanel = new E(cs[1]);
34208         this.headerPanel.enableDisplayMode("block");
34209
34210         this.scroller = new E(cs[2]);
34211         this.scrollSizer = new E(this.scroller.dom.firstChild);
34212
34213         this.lockedWrap = new E(cs[3]);
34214         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34215         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34216
34217         this.mainWrap = new E(cs[4]);
34218         this.mainHd = new E(this.mainWrap.dom.firstChild);
34219         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34220
34221         this.footerPanel = new E(cs[5]);
34222         this.footerPanel.enableDisplayMode("block");
34223
34224         this.resizeProxy = new E(cs[6]);
34225
34226         this.headerSelector = String.format(
34227            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34228            this.lockedHd.id, this.mainHd.id
34229         );
34230
34231         this.splitterSelector = String.format(
34232            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34233            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34234         );
34235     },
34236     idToCssName : function(s)
34237     {
34238         return s.replace(/[^a-z0-9]+/ig, '-');
34239     },
34240
34241     getHeaderCell : function(index){
34242         return Roo.DomQuery.select(this.headerSelector)[index];
34243     },
34244
34245     getHeaderCellMeasure : function(index){
34246         return this.getHeaderCell(index).firstChild;
34247     },
34248
34249     getHeaderCellText : function(index){
34250         return this.getHeaderCell(index).firstChild.firstChild;
34251     },
34252
34253     getLockedTable : function(){
34254         return this.lockedBody.dom.firstChild;
34255     },
34256
34257     getBodyTable : function(){
34258         return this.mainBody.dom.firstChild;
34259     },
34260
34261     getLockedRow : function(index){
34262         return this.getLockedTable().rows[index];
34263     },
34264
34265     getRow : function(index){
34266         return this.getBodyTable().rows[index];
34267     },
34268
34269     getRowComposite : function(index){
34270         if(!this.rowEl){
34271             this.rowEl = new Roo.CompositeElementLite();
34272         }
34273         var els = [], lrow, mrow;
34274         if(lrow = this.getLockedRow(index)){
34275             els.push(lrow);
34276         }
34277         if(mrow = this.getRow(index)){
34278             els.push(mrow);
34279         }
34280         this.rowEl.elements = els;
34281         return this.rowEl;
34282     },
34283     /**
34284      * Gets the 'td' of the cell
34285      * 
34286      * @param {Integer} rowIndex row to select
34287      * @param {Integer} colIndex column to select
34288      * 
34289      * @return {Object} 
34290      */
34291     getCell : function(rowIndex, colIndex){
34292         var locked = this.cm.getLockedCount();
34293         var source;
34294         if(colIndex < locked){
34295             source = this.lockedBody.dom.firstChild;
34296         }else{
34297             source = this.mainBody.dom.firstChild;
34298             colIndex -= locked;
34299         }
34300         return source.rows[rowIndex].childNodes[colIndex];
34301     },
34302
34303     getCellText : function(rowIndex, colIndex){
34304         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
34305     },
34306
34307     getCellBox : function(cell){
34308         var b = this.fly(cell).getBox();
34309         if(Roo.isOpera){ // opera fails to report the Y
34310             b.y = cell.offsetTop + this.mainBody.getY();
34311         }
34312         return b;
34313     },
34314
34315     getCellIndex : function(cell){
34316         var id = String(cell.className).match(this.cellRE);
34317         if(id){
34318             return parseInt(id[1], 10);
34319         }
34320         return 0;
34321     },
34322
34323     findHeaderIndex : function(n){
34324         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34325         return r ? this.getCellIndex(r) : false;
34326     },
34327
34328     findHeaderCell : function(n){
34329         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
34330         return r ? r : false;
34331     },
34332
34333     findRowIndex : function(n){
34334         if(!n){
34335             return false;
34336         }
34337         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
34338         return r ? r.rowIndex : false;
34339     },
34340
34341     findCellIndex : function(node){
34342         var stop = this.el.dom;
34343         while(node && node != stop){
34344             if(this.findRE.test(node.className)){
34345                 return this.getCellIndex(node);
34346             }
34347             node = node.parentNode;
34348         }
34349         return false;
34350     },
34351
34352     getColumnId : function(index){
34353         return this.cm.getColumnId(index);
34354     },
34355
34356     getSplitters : function()
34357     {
34358         if(this.splitterSelector){
34359            return Roo.DomQuery.select(this.splitterSelector);
34360         }else{
34361             return null;
34362       }
34363     },
34364
34365     getSplitter : function(index){
34366         return this.getSplitters()[index];
34367     },
34368
34369     onRowOver : function(e, t){
34370         var row;
34371         if((row = this.findRowIndex(t)) !== false){
34372             this.getRowComposite(row).addClass("x-grid-row-over");
34373         }
34374     },
34375
34376     onRowOut : function(e, t){
34377         var row;
34378         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
34379             this.getRowComposite(row).removeClass("x-grid-row-over");
34380         }
34381     },
34382
34383     renderHeaders : function(){
34384         var cm = this.cm;
34385         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
34386         var cb = [], lb = [], sb = [], lsb = [], p = {};
34387         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34388             p.cellId = "x-grid-hd-0-" + i;
34389             p.splitId = "x-grid-csplit-0-" + i;
34390             p.id = cm.getColumnId(i);
34391             p.title = cm.getColumnTooltip(i) || "";
34392             p.value = cm.getColumnHeader(i) || "";
34393             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
34394             if(!cm.isLocked(i)){
34395                 cb[cb.length] = ct.apply(p);
34396                 sb[sb.length] = st.apply(p);
34397             }else{
34398                 lb[lb.length] = ct.apply(p);
34399                 lsb[lsb.length] = st.apply(p);
34400             }
34401         }
34402         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
34403                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
34404     },
34405
34406     updateHeaders : function(){
34407         var html = this.renderHeaders();
34408         this.lockedHd.update(html[0]);
34409         this.mainHd.update(html[1]);
34410     },
34411
34412     /**
34413      * Focuses the specified row.
34414      * @param {Number} row The row index
34415      */
34416     focusRow : function(row)
34417     {
34418         //Roo.log('GridView.focusRow');
34419         var x = this.scroller.dom.scrollLeft;
34420         this.focusCell(row, 0, false);
34421         this.scroller.dom.scrollLeft = x;
34422     },
34423
34424     /**
34425      * Focuses the specified cell.
34426      * @param {Number} row The row index
34427      * @param {Number} col The column index
34428      * @param {Boolean} hscroll false to disable horizontal scrolling
34429      */
34430     focusCell : function(row, col, hscroll)
34431     {
34432         //Roo.log('GridView.focusCell');
34433         var el = this.ensureVisible(row, col, hscroll);
34434         this.focusEl.alignTo(el, "tl-tl");
34435         if(Roo.isGecko){
34436             this.focusEl.focus();
34437         }else{
34438             this.focusEl.focus.defer(1, this.focusEl);
34439         }
34440     },
34441
34442     /**
34443      * Scrolls the specified cell into view
34444      * @param {Number} row The row index
34445      * @param {Number} col The column index
34446      * @param {Boolean} hscroll false to disable horizontal scrolling
34447      */
34448     ensureVisible : function(row, col, hscroll)
34449     {
34450         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34451         //return null; //disable for testing.
34452         if(typeof row != "number"){
34453             row = row.rowIndex;
34454         }
34455         if(row < 0 && row >= this.ds.getCount()){
34456             return  null;
34457         }
34458         col = (col !== undefined ? col : 0);
34459         var cm = this.grid.colModel;
34460         while(cm.isHidden(col)){
34461             col++;
34462         }
34463
34464         var el = this.getCell(row, col);
34465         if(!el){
34466             return null;
34467         }
34468         var c = this.scroller.dom;
34469
34470         var ctop = parseInt(el.offsetTop, 10);
34471         var cleft = parseInt(el.offsetLeft, 10);
34472         var cbot = ctop + el.offsetHeight;
34473         var cright = cleft + el.offsetWidth;
34474         
34475         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34476         var stop = parseInt(c.scrollTop, 10);
34477         var sleft = parseInt(c.scrollLeft, 10);
34478         var sbot = stop + ch;
34479         var sright = sleft + c.clientWidth;
34480         /*
34481         Roo.log('GridView.ensureVisible:' +
34482                 ' ctop:' + ctop +
34483                 ' c.clientHeight:' + c.clientHeight +
34484                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34485                 ' stop:' + stop +
34486                 ' cbot:' + cbot +
34487                 ' sbot:' + sbot +
34488                 ' ch:' + ch  
34489                 );
34490         */
34491         if(ctop < stop){
34492              c.scrollTop = ctop;
34493             //Roo.log("set scrolltop to ctop DISABLE?");
34494         }else if(cbot > sbot){
34495             //Roo.log("set scrolltop to cbot-ch");
34496             c.scrollTop = cbot-ch;
34497         }
34498         
34499         if(hscroll !== false){
34500             if(cleft < sleft){
34501                 c.scrollLeft = cleft;
34502             }else if(cright > sright){
34503                 c.scrollLeft = cright-c.clientWidth;
34504             }
34505         }
34506          
34507         return el;
34508     },
34509
34510     updateColumns : function(){
34511         this.grid.stopEditing();
34512         var cm = this.grid.colModel, colIds = this.getColumnIds();
34513         //var totalWidth = cm.getTotalWidth();
34514         var pos = 0;
34515         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34516             //if(cm.isHidden(i)) continue;
34517             var w = cm.getColumnWidth(i);
34518             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34519             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34520         }
34521         this.updateSplitters();
34522     },
34523
34524     generateRules : function(cm){
34525         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34526         Roo.util.CSS.removeStyleSheet(rulesId);
34527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34528             var cid = cm.getColumnId(i);
34529             var align = '';
34530             if(cm.config[i].align){
34531                 align = 'text-align:'+cm.config[i].align+';';
34532             }
34533             var hidden = '';
34534             if(cm.isHidden(i)){
34535                 hidden = 'display:none;';
34536             }
34537             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34538             ruleBuf.push(
34539                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34540                     this.hdSelector, cid, " {\n", align, width, "}\n",
34541                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34542                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34543         }
34544         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34545     },
34546
34547     updateSplitters : function(){
34548         var cm = this.cm, s = this.getSplitters();
34549         if(s){ // splitters not created yet
34550             var pos = 0, locked = true;
34551             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34552                 if(cm.isHidden(i)) continue;
34553                 var w = cm.getColumnWidth(i); // make sure it's a number
34554                 if(!cm.isLocked(i) && locked){
34555                     pos = 0;
34556                     locked = false;
34557                 }
34558                 pos += w;
34559                 s[i].style.left = (pos-this.splitOffset) + "px";
34560             }
34561         }
34562     },
34563
34564     handleHiddenChange : function(colModel, colIndex, hidden){
34565         if(hidden){
34566             this.hideColumn(colIndex);
34567         }else{
34568             this.unhideColumn(colIndex);
34569         }
34570     },
34571
34572     hideColumn : function(colIndex){
34573         var cid = this.getColumnId(colIndex);
34574         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34575         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34576         if(Roo.isSafari){
34577             this.updateHeaders();
34578         }
34579         this.updateSplitters();
34580         this.layout();
34581     },
34582
34583     unhideColumn : function(colIndex){
34584         var cid = this.getColumnId(colIndex);
34585         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34586         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34587
34588         if(Roo.isSafari){
34589             this.updateHeaders();
34590         }
34591         this.updateSplitters();
34592         this.layout();
34593     },
34594
34595     insertRows : function(dm, firstRow, lastRow, isUpdate){
34596         if(firstRow == 0 && lastRow == dm.getCount()-1){
34597             this.refresh();
34598         }else{
34599             if(!isUpdate){
34600                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34601             }
34602             var s = this.getScrollState();
34603             var markup = this.renderRows(firstRow, lastRow);
34604             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34605             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34606             this.restoreScroll(s);
34607             if(!isUpdate){
34608                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34609                 this.syncRowHeights(firstRow, lastRow);
34610                 this.stripeRows(firstRow);
34611                 this.layout();
34612             }
34613         }
34614     },
34615
34616     bufferRows : function(markup, target, index){
34617         var before = null, trows = target.rows, tbody = target.tBodies[0];
34618         if(index < trows.length){
34619             before = trows[index];
34620         }
34621         var b = document.createElement("div");
34622         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34623         var rows = b.firstChild.rows;
34624         for(var i = 0, len = rows.length; i < len; i++){
34625             if(before){
34626                 tbody.insertBefore(rows[0], before);
34627             }else{
34628                 tbody.appendChild(rows[0]);
34629             }
34630         }
34631         b.innerHTML = "";
34632         b = null;
34633     },
34634
34635     deleteRows : function(dm, firstRow, lastRow){
34636         if(dm.getRowCount()<1){
34637             this.fireEvent("beforerefresh", this);
34638             this.mainBody.update("");
34639             this.lockedBody.update("");
34640             this.fireEvent("refresh", this);
34641         }else{
34642             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34643             var bt = this.getBodyTable();
34644             var tbody = bt.firstChild;
34645             var rows = bt.rows;
34646             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34647                 tbody.removeChild(rows[firstRow]);
34648             }
34649             this.stripeRows(firstRow);
34650             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34651         }
34652     },
34653
34654     updateRows : function(dataSource, firstRow, lastRow){
34655         var s = this.getScrollState();
34656         this.refresh();
34657         this.restoreScroll(s);
34658     },
34659
34660     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34661         if(!noRefresh){
34662            this.refresh();
34663         }
34664         this.updateHeaderSortState();
34665     },
34666
34667     getScrollState : function(){
34668         
34669         var sb = this.scroller.dom;
34670         return {left: sb.scrollLeft, top: sb.scrollTop};
34671     },
34672
34673     stripeRows : function(startRow){
34674         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34675             return;
34676         }
34677         startRow = startRow || 0;
34678         var rows = this.getBodyTable().rows;
34679         var lrows = this.getLockedTable().rows;
34680         var cls = ' x-grid-row-alt ';
34681         for(var i = startRow, len = rows.length; i < len; i++){
34682             var row = rows[i], lrow = lrows[i];
34683             var isAlt = ((i+1) % 2 == 0);
34684             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34685             if(isAlt == hasAlt){
34686                 continue;
34687             }
34688             if(isAlt){
34689                 row.className += " x-grid-row-alt";
34690             }else{
34691                 row.className = row.className.replace("x-grid-row-alt", "");
34692             }
34693             if(lrow){
34694                 lrow.className = row.className;
34695             }
34696         }
34697     },
34698
34699     restoreScroll : function(state){
34700         //Roo.log('GridView.restoreScroll');
34701         var sb = this.scroller.dom;
34702         sb.scrollLeft = state.left;
34703         sb.scrollTop = state.top;
34704         this.syncScroll();
34705     },
34706
34707     syncScroll : function(){
34708         //Roo.log('GridView.syncScroll');
34709         var sb = this.scroller.dom;
34710         var sh = this.mainHd.dom;
34711         var bs = this.mainBody.dom;
34712         var lv = this.lockedBody.dom;
34713         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34714         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34715     },
34716
34717     handleScroll : function(e){
34718         this.syncScroll();
34719         var sb = this.scroller.dom;
34720         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34721         e.stopEvent();
34722     },
34723
34724     handleWheel : function(e){
34725         var d = e.getWheelDelta();
34726         this.scroller.dom.scrollTop -= d*22;
34727         // set this here to prevent jumpy scrolling on large tables
34728         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34729         e.stopEvent();
34730     },
34731
34732     renderRows : function(startRow, endRow){
34733         // pull in all the crap needed to render rows
34734         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34735         var colCount = cm.getColumnCount();
34736
34737         if(ds.getCount() < 1){
34738             return ["", ""];
34739         }
34740
34741         // build a map for all the columns
34742         var cs = [];
34743         for(var i = 0; i < colCount; i++){
34744             var name = cm.getDataIndex(i);
34745             cs[i] = {
34746                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34747                 renderer : cm.getRenderer(i),
34748                 id : cm.getColumnId(i),
34749                 locked : cm.isLocked(i)
34750             };
34751         }
34752
34753         startRow = startRow || 0;
34754         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34755
34756         // records to render
34757         var rs = ds.getRange(startRow, endRow);
34758
34759         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34760     },
34761
34762     // As much as I hate to duplicate code, this was branched because FireFox really hates
34763     // [].join("") on strings. The performance difference was substantial enough to
34764     // branch this function
34765     doRender : Roo.isGecko ?
34766             function(cs, rs, ds, startRow, colCount, stripe){
34767                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34768                 // buffers
34769                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34770                 
34771                 var hasListener = this.grid.hasListener('rowclass');
34772                 var rowcfg = {};
34773                 for(var j = 0, len = rs.length; j < len; j++){
34774                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34775                     for(var i = 0; i < colCount; i++){
34776                         c = cs[i];
34777                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34778                         p.id = c.id;
34779                         p.css = p.attr = "";
34780                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34781                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34782                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34783                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34784                         }
34785                         var markup = ct.apply(p);
34786                         if(!c.locked){
34787                             cb+= markup;
34788                         }else{
34789                             lcb+= markup;
34790                         }
34791                     }
34792                     var alt = [];
34793                     if(stripe && ((rowIndex+1) % 2 == 0)){
34794                         alt.push("x-grid-row-alt")
34795                     }
34796                     if(r.dirty){
34797                         alt.push(  " x-grid-dirty-row");
34798                     }
34799                     rp.cells = lcb;
34800                     if(this.getRowClass){
34801                         alt.push(this.getRowClass(r, rowIndex));
34802                     }
34803                     if (hasListener) {
34804                         rowcfg = {
34805                              
34806                             record: r,
34807                             rowIndex : rowIndex,
34808                             rowClass : ''
34809                         }
34810                         this.grid.fireEvent('rowclass', this, rowcfg);
34811                         alt.push(rowcfg.rowClass);
34812                     }
34813                     rp.alt = alt.join(" ");
34814                     lbuf+= rt.apply(rp);
34815                     rp.cells = cb;
34816                     buf+=  rt.apply(rp);
34817                 }
34818                 return [lbuf, buf];
34819             } :
34820             function(cs, rs, ds, startRow, colCount, stripe){
34821                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34822                 // buffers
34823                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34824                 var hasListener = this.grid.hasListener('rowclass');
34825                 var rowcfg = {};
34826                 for(var j = 0, len = rs.length; j < len; j++){
34827                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34828                     for(var i = 0; i < colCount; i++){
34829                         c = cs[i];
34830                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34831                         p.id = c.id;
34832                         p.css = p.attr = "";
34833                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34834                         if(p.value == undefined || p.value === "") p.value = "&#160;";
34835                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34836                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
34837                         }
34838                         var markup = ct.apply(p);
34839                         if(!c.locked){
34840                             cb[cb.length] = markup;
34841                         }else{
34842                             lcb[lcb.length] = markup;
34843                         }
34844                     }
34845                     var alt = [];
34846                     if(stripe && ((rowIndex+1) % 2 == 0)){
34847                         alt.push( "x-grid-row-alt");
34848                     }
34849                     if(r.dirty){
34850                         alt.push(" x-grid-dirty-row");
34851                     }
34852                     rp.cells = lcb;
34853                     if(this.getRowClass){
34854                         alt.push( this.getRowClass(r, rowIndex));
34855                     }
34856                     if (hasListener) {
34857                         rowcfg = {
34858                              
34859                             record: r,
34860                             rowIndex : rowIndex,
34861                             rowClass : ''
34862                         }
34863                         this.grid.fireEvent('rowclass', this, rowcfg);
34864                         alt.push(rowcfg.rowClass);
34865                     }
34866                     rp.alt = alt.join(" ");
34867                     rp.cells = lcb.join("");
34868                     lbuf[lbuf.length] = rt.apply(rp);
34869                     rp.cells = cb.join("");
34870                     buf[buf.length] =  rt.apply(rp);
34871                 }
34872                 return [lbuf.join(""), buf.join("")];
34873             },
34874
34875     renderBody : function(){
34876         var markup = this.renderRows();
34877         var bt = this.templates.body;
34878         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34879     },
34880
34881     /**
34882      * Refreshes the grid
34883      * @param {Boolean} headersToo
34884      */
34885     refresh : function(headersToo){
34886         this.fireEvent("beforerefresh", this);
34887         this.grid.stopEditing();
34888         var result = this.renderBody();
34889         this.lockedBody.update(result[0]);
34890         this.mainBody.update(result[1]);
34891         if(headersToo === true){
34892             this.updateHeaders();
34893             this.updateColumns();
34894             this.updateSplitters();
34895             this.updateHeaderSortState();
34896         }
34897         this.syncRowHeights();
34898         this.layout();
34899         this.fireEvent("refresh", this);
34900     },
34901
34902     handleColumnMove : function(cm, oldIndex, newIndex){
34903         this.indexMap = null;
34904         var s = this.getScrollState();
34905         this.refresh(true);
34906         this.restoreScroll(s);
34907         this.afterMove(newIndex);
34908     },
34909
34910     afterMove : function(colIndex){
34911         if(this.enableMoveAnim && Roo.enableFx){
34912             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34913         }
34914         // if multisort - fix sortOrder, and reload..
34915         if (this.grid.dataSource.multiSort) {
34916             // the we can call sort again..
34917             var dm = this.grid.dataSource;
34918             var cm = this.grid.colModel;
34919             var so = [];
34920             for(var i = 0; i < cm.config.length; i++ ) {
34921                 
34922                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34923                     continue; // dont' bother, it's not in sort list or being set.
34924                 }
34925                 
34926                 so.push(cm.config[i].dataIndex);
34927             };
34928             dm.sortOrder = so;
34929             dm.load(dm.lastOptions);
34930             
34931             
34932         }
34933         
34934     },
34935
34936     updateCell : function(dm, rowIndex, dataIndex){
34937         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34938         if(typeof colIndex == "undefined"){ // not present in grid
34939             return;
34940         }
34941         var cm = this.grid.colModel;
34942         var cell = this.getCell(rowIndex, colIndex);
34943         var cellText = this.getCellText(rowIndex, colIndex);
34944
34945         var p = {
34946             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34947             id : cm.getColumnId(colIndex),
34948             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34949         };
34950         var renderer = cm.getRenderer(colIndex);
34951         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34952         if(typeof val == "undefined" || val === "") val = "&#160;";
34953         cellText.innerHTML = val;
34954         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34955         this.syncRowHeights(rowIndex, rowIndex);
34956     },
34957
34958     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34959         var maxWidth = 0;
34960         if(this.grid.autoSizeHeaders){
34961             var h = this.getHeaderCellMeasure(colIndex);
34962             maxWidth = Math.max(maxWidth, h.scrollWidth);
34963         }
34964         var tb, index;
34965         if(this.cm.isLocked(colIndex)){
34966             tb = this.getLockedTable();
34967             index = colIndex;
34968         }else{
34969             tb = this.getBodyTable();
34970             index = colIndex - this.cm.getLockedCount();
34971         }
34972         if(tb && tb.rows){
34973             var rows = tb.rows;
34974             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34975             for(var i = 0; i < stopIndex; i++){
34976                 var cell = rows[i].childNodes[index].firstChild;
34977                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34978             }
34979         }
34980         return maxWidth + /*margin for error in IE*/ 5;
34981     },
34982     /**
34983      * Autofit a column to its content.
34984      * @param {Number} colIndex
34985      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34986      */
34987      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34988          if(this.cm.isHidden(colIndex)){
34989              return; // can't calc a hidden column
34990          }
34991         if(forceMinSize){
34992             var cid = this.cm.getColumnId(colIndex);
34993             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34994            if(this.grid.autoSizeHeaders){
34995                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34996            }
34997         }
34998         var newWidth = this.calcColumnWidth(colIndex);
34999         this.cm.setColumnWidth(colIndex,
35000             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35001         if(!suppressEvent){
35002             this.grid.fireEvent("columnresize", colIndex, newWidth);
35003         }
35004     },
35005
35006     /**
35007      * Autofits all columns to their content and then expands to fit any extra space in the grid
35008      */
35009      autoSizeColumns : function(){
35010         var cm = this.grid.colModel;
35011         var colCount = cm.getColumnCount();
35012         for(var i = 0; i < colCount; i++){
35013             this.autoSizeColumn(i, true, true);
35014         }
35015         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35016             this.fitColumns();
35017         }else{
35018             this.updateColumns();
35019             this.layout();
35020         }
35021     },
35022
35023     /**
35024      * Autofits all columns to the grid's width proportionate with their current size
35025      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35026      */
35027     fitColumns : function(reserveScrollSpace){
35028         var cm = this.grid.colModel;
35029         var colCount = cm.getColumnCount();
35030         var cols = [];
35031         var width = 0;
35032         var i, w;
35033         for (i = 0; i < colCount; i++){
35034             if(!cm.isHidden(i) && !cm.isFixed(i)){
35035                 w = cm.getColumnWidth(i);
35036                 cols.push(i);
35037                 cols.push(w);
35038                 width += w;
35039             }
35040         }
35041         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35042         if(reserveScrollSpace){
35043             avail -= 17;
35044         }
35045         var frac = (avail - cm.getTotalWidth())/width;
35046         while (cols.length){
35047             w = cols.pop();
35048             i = cols.pop();
35049             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35050         }
35051         this.updateColumns();
35052         this.layout();
35053     },
35054
35055     onRowSelect : function(rowIndex){
35056         var row = this.getRowComposite(rowIndex);
35057         row.addClass("x-grid-row-selected");
35058     },
35059
35060     onRowDeselect : function(rowIndex){
35061         var row = this.getRowComposite(rowIndex);
35062         row.removeClass("x-grid-row-selected");
35063     },
35064
35065     onCellSelect : function(row, col){
35066         var cell = this.getCell(row, col);
35067         if(cell){
35068             Roo.fly(cell).addClass("x-grid-cell-selected");
35069         }
35070     },
35071
35072     onCellDeselect : function(row, col){
35073         var cell = this.getCell(row, col);
35074         if(cell){
35075             Roo.fly(cell).removeClass("x-grid-cell-selected");
35076         }
35077     },
35078
35079     updateHeaderSortState : function(){
35080         
35081         // sort state can be single { field: xxx, direction : yyy}
35082         // or   { xxx=>ASC , yyy : DESC ..... }
35083         
35084         var mstate = {};
35085         if (!this.ds.multiSort) { 
35086             var state = this.ds.getSortState();
35087             if(!state){
35088                 return;
35089             }
35090             mstate[state.field] = state.direction;
35091             // FIXME... - this is not used here.. but might be elsewhere..
35092             this.sortState = state;
35093             
35094         } else {
35095             mstate = this.ds.sortToggle;
35096         }
35097         //remove existing sort classes..
35098         
35099         var sc = this.sortClasses;
35100         var hds = this.el.select(this.headerSelector).removeClass(sc);
35101         
35102         for(var f in mstate) {
35103         
35104             var sortColumn = this.cm.findColumnIndex(f);
35105             
35106             if(sortColumn != -1){
35107                 var sortDir = mstate[f];        
35108                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35109             }
35110         }
35111         
35112          
35113         
35114     },
35115
35116
35117     handleHeaderClick : function(g, index){
35118         if(this.headersDisabled){
35119             return;
35120         }
35121         var dm = g.dataSource, cm = g.colModel;
35122         if(!cm.isSortable(index)){
35123             return;
35124         }
35125         g.stopEditing();
35126         
35127         if (dm.multiSort) {
35128             // update the sortOrder
35129             var so = [];
35130             for(var i = 0; i < cm.config.length; i++ ) {
35131                 
35132                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35133                     continue; // dont' bother, it's not in sort list or being set.
35134                 }
35135                 
35136                 so.push(cm.config[i].dataIndex);
35137             };
35138             dm.sortOrder = so;
35139         }
35140         
35141         
35142         dm.sort(cm.getDataIndex(index));
35143     },
35144
35145
35146     destroy : function(){
35147         if(this.colMenu){
35148             this.colMenu.removeAll();
35149             Roo.menu.MenuMgr.unregister(this.colMenu);
35150             this.colMenu.getEl().remove();
35151             delete this.colMenu;
35152         }
35153         if(this.hmenu){
35154             this.hmenu.removeAll();
35155             Roo.menu.MenuMgr.unregister(this.hmenu);
35156             this.hmenu.getEl().remove();
35157             delete this.hmenu;
35158         }
35159         if(this.grid.enableColumnMove){
35160             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35161             if(dds){
35162                 for(var dd in dds){
35163                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35164                         var elid = dds[dd].dragElId;
35165                         dds[dd].unreg();
35166                         Roo.get(elid).remove();
35167                     } else if(dds[dd].config.isTarget){
35168                         dds[dd].proxyTop.remove();
35169                         dds[dd].proxyBottom.remove();
35170                         dds[dd].unreg();
35171                     }
35172                     if(Roo.dd.DDM.locationCache[dd]){
35173                         delete Roo.dd.DDM.locationCache[dd];
35174                     }
35175                 }
35176                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35177             }
35178         }
35179         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35180         this.bind(null, null);
35181         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35182     },
35183
35184     handleLockChange : function(){
35185         this.refresh(true);
35186     },
35187
35188     onDenyColumnLock : function(){
35189
35190     },
35191
35192     onDenyColumnHide : function(){
35193
35194     },
35195
35196     handleHdMenuClick : function(item){
35197         var index = this.hdCtxIndex;
35198         var cm = this.cm, ds = this.ds;
35199         switch(item.id){
35200             case "asc":
35201                 ds.sort(cm.getDataIndex(index), "ASC");
35202                 break;
35203             case "desc":
35204                 ds.sort(cm.getDataIndex(index), "DESC");
35205                 break;
35206             case "lock":
35207                 var lc = cm.getLockedCount();
35208                 if(cm.getColumnCount(true) <= lc+1){
35209                     this.onDenyColumnLock();
35210                     return;
35211                 }
35212                 if(lc != index){
35213                     cm.setLocked(index, true, true);
35214                     cm.moveColumn(index, lc);
35215                     this.grid.fireEvent("columnmove", index, lc);
35216                 }else{
35217                     cm.setLocked(index, true);
35218                 }
35219             break;
35220             case "unlock":
35221                 var lc = cm.getLockedCount();
35222                 if((lc-1) != index){
35223                     cm.setLocked(index, false, true);
35224                     cm.moveColumn(index, lc-1);
35225                     this.grid.fireEvent("columnmove", index, lc-1);
35226                 }else{
35227                     cm.setLocked(index, false);
35228                 }
35229             break;
35230             default:
35231                 index = cm.getIndexById(item.id.substr(4));
35232                 if(index != -1){
35233                     if(item.checked && cm.getColumnCount(true) <= 1){
35234                         this.onDenyColumnHide();
35235                         return false;
35236                     }
35237                     cm.setHidden(index, item.checked);
35238                 }
35239         }
35240         return true;
35241     },
35242
35243     beforeColMenuShow : function(){
35244         var cm = this.cm,  colCount = cm.getColumnCount();
35245         this.colMenu.removeAll();
35246         for(var i = 0; i < colCount; i++){
35247             this.colMenu.add(new Roo.menu.CheckItem({
35248                 id: "col-"+cm.getColumnId(i),
35249                 text: cm.getColumnHeader(i),
35250                 checked: !cm.isHidden(i),
35251                 hideOnClick:false
35252             }));
35253         }
35254     },
35255
35256     handleHdCtx : function(g, index, e){
35257         e.stopEvent();
35258         var hd = this.getHeaderCell(index);
35259         this.hdCtxIndex = index;
35260         var ms = this.hmenu.items, cm = this.cm;
35261         ms.get("asc").setDisabled(!cm.isSortable(index));
35262         ms.get("desc").setDisabled(!cm.isSortable(index));
35263         if(this.grid.enableColLock !== false){
35264             ms.get("lock").setDisabled(cm.isLocked(index));
35265             ms.get("unlock").setDisabled(!cm.isLocked(index));
35266         }
35267         this.hmenu.show(hd, "tl-bl");
35268     },
35269
35270     handleHdOver : function(e){
35271         var hd = this.findHeaderCell(e.getTarget());
35272         if(hd && !this.headersDisabled){
35273             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35274                this.fly(hd).addClass("x-grid-hd-over");
35275             }
35276         }
35277     },
35278
35279     handleHdOut : function(e){
35280         var hd = this.findHeaderCell(e.getTarget());
35281         if(hd){
35282             this.fly(hd).removeClass("x-grid-hd-over");
35283         }
35284     },
35285
35286     handleSplitDblClick : function(e, t){
35287         var i = this.getCellIndex(t);
35288         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35289             this.autoSizeColumn(i, true);
35290             this.layout();
35291         }
35292     },
35293
35294     render : function(){
35295
35296         var cm = this.cm;
35297         var colCount = cm.getColumnCount();
35298
35299         if(this.grid.monitorWindowResize === true){
35300             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35301         }
35302         var header = this.renderHeaders();
35303         var body = this.templates.body.apply({rows:""});
35304         var html = this.templates.master.apply({
35305             lockedBody: body,
35306             body: body,
35307             lockedHeader: header[0],
35308             header: header[1]
35309         });
35310
35311         //this.updateColumns();
35312
35313         this.grid.getGridEl().dom.innerHTML = html;
35314
35315         this.initElements();
35316         
35317         // a kludge to fix the random scolling effect in webkit
35318         this.el.on("scroll", function() {
35319             this.el.dom.scrollTop=0; // hopefully not recursive..
35320         },this);
35321
35322         this.scroller.on("scroll", this.handleScroll, this);
35323         this.lockedBody.on("mousewheel", this.handleWheel, this);
35324         this.mainBody.on("mousewheel", this.handleWheel, this);
35325
35326         this.mainHd.on("mouseover", this.handleHdOver, this);
35327         this.mainHd.on("mouseout", this.handleHdOut, this);
35328         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
35329                 {delegate: "."+this.splitClass});
35330
35331         this.lockedHd.on("mouseover", this.handleHdOver, this);
35332         this.lockedHd.on("mouseout", this.handleHdOut, this);
35333         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
35334                 {delegate: "."+this.splitClass});
35335
35336         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
35337             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35338         }
35339
35340         this.updateSplitters();
35341
35342         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
35343             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35344             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
35345         }
35346
35347         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
35348             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
35349             this.hmenu.add(
35350                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
35351                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
35352             );
35353             if(this.grid.enableColLock !== false){
35354                 this.hmenu.add('-',
35355                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
35356                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
35357                 );
35358             }
35359             if(this.grid.enableColumnHide !== false){
35360
35361                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
35362                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
35363                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
35364
35365                 this.hmenu.add('-',
35366                     {id:"columns", text: this.columnsText, menu: this.colMenu}
35367                 );
35368             }
35369             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
35370
35371             this.grid.on("headercontextmenu", this.handleHdCtx, this);
35372         }
35373
35374         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
35375             this.dd = new Roo.grid.GridDragZone(this.grid, {
35376                 ddGroup : this.grid.ddGroup || 'GridDD'
35377             });
35378         }
35379
35380         /*
35381         for(var i = 0; i < colCount; i++){
35382             if(cm.isHidden(i)){
35383                 this.hideColumn(i);
35384             }
35385             if(cm.config[i].align){
35386                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35387                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35388             }
35389         }*/
35390         
35391         this.updateHeaderSortState();
35392
35393         this.beforeInitialResize();
35394         this.layout(true);
35395
35396         // two part rendering gives faster view to the user
35397         this.renderPhase2.defer(1, this);
35398     },
35399
35400     renderPhase2 : function(){
35401         // render the rows now
35402         this.refresh();
35403         if(this.grid.autoSizeColumns){
35404             this.autoSizeColumns();
35405         }
35406     },
35407
35408     beforeInitialResize : function(){
35409
35410     },
35411
35412     onColumnSplitterMoved : function(i, w){
35413         this.userResized = true;
35414         var cm = this.grid.colModel;
35415         cm.setColumnWidth(i, w, true);
35416         var cid = cm.getColumnId(i);
35417         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35418         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35419         this.updateSplitters();
35420         this.layout();
35421         this.grid.fireEvent("columnresize", i, w);
35422     },
35423
35424     syncRowHeights : function(startIndex, endIndex){
35425         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35426             startIndex = startIndex || 0;
35427             var mrows = this.getBodyTable().rows;
35428             var lrows = this.getLockedTable().rows;
35429             var len = mrows.length-1;
35430             endIndex = Math.min(endIndex || len, len);
35431             for(var i = startIndex; i <= endIndex; i++){
35432                 var m = mrows[i], l = lrows[i];
35433                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35434                 m.style.height = l.style.height = h + "px";
35435             }
35436         }
35437     },
35438
35439     layout : function(initialRender, is2ndPass){
35440         var g = this.grid;
35441         var auto = g.autoHeight;
35442         var scrollOffset = 16;
35443         var c = g.getGridEl(), cm = this.cm,
35444                 expandCol = g.autoExpandColumn,
35445                 gv = this;
35446         //c.beginMeasure();
35447
35448         if(!c.dom.offsetWidth){ // display:none?
35449             if(initialRender){
35450                 this.lockedWrap.show();
35451                 this.mainWrap.show();
35452             }
35453             return;
35454         }
35455
35456         var hasLock = this.cm.isLocked(0);
35457
35458         var tbh = this.headerPanel.getHeight();
35459         var bbh = this.footerPanel.getHeight();
35460
35461         if(auto){
35462             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35463             var newHeight = ch + c.getBorderWidth("tb");
35464             if(g.maxHeight){
35465                 newHeight = Math.min(g.maxHeight, newHeight);
35466             }
35467             c.setHeight(newHeight);
35468         }
35469
35470         if(g.autoWidth){
35471             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35472         }
35473
35474         var s = this.scroller;
35475
35476         var csize = c.getSize(true);
35477
35478         this.el.setSize(csize.width, csize.height);
35479
35480         this.headerPanel.setWidth(csize.width);
35481         this.footerPanel.setWidth(csize.width);
35482
35483         var hdHeight = this.mainHd.getHeight();
35484         var vw = csize.width;
35485         var vh = csize.height - (tbh + bbh);
35486
35487         s.setSize(vw, vh);
35488
35489         var bt = this.getBodyTable();
35490         var ltWidth = hasLock ?
35491                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35492
35493         var scrollHeight = bt.offsetHeight;
35494         var scrollWidth = ltWidth + bt.offsetWidth;
35495         var vscroll = false, hscroll = false;
35496
35497         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35498
35499         var lw = this.lockedWrap, mw = this.mainWrap;
35500         var lb = this.lockedBody, mb = this.mainBody;
35501
35502         setTimeout(function(){
35503             var t = s.dom.offsetTop;
35504             var w = s.dom.clientWidth,
35505                 h = s.dom.clientHeight;
35506
35507             lw.setTop(t);
35508             lw.setSize(ltWidth, h);
35509
35510             mw.setLeftTop(ltWidth, t);
35511             mw.setSize(w-ltWidth, h);
35512
35513             lb.setHeight(h-hdHeight);
35514             mb.setHeight(h-hdHeight);
35515
35516             if(is2ndPass !== true && !gv.userResized && expandCol){
35517                 // high speed resize without full column calculation
35518                 
35519                 var ci = cm.getIndexById(expandCol);
35520                 if (ci < 0) {
35521                     ci = cm.findColumnIndex(expandCol);
35522                 }
35523                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35524                 var expandId = cm.getColumnId(ci);
35525                 var  tw = cm.getTotalWidth(false);
35526                 var currentWidth = cm.getColumnWidth(ci);
35527                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35528                 if(currentWidth != cw){
35529                     cm.setColumnWidth(ci, cw, true);
35530                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35531                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35532                     gv.updateSplitters();
35533                     gv.layout(false, true);
35534                 }
35535             }
35536
35537             if(initialRender){
35538                 lw.show();
35539                 mw.show();
35540             }
35541             //c.endMeasure();
35542         }, 10);
35543     },
35544
35545     onWindowResize : function(){
35546         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35547             return;
35548         }
35549         this.layout();
35550     },
35551
35552     appendFooter : function(parentEl){
35553         return null;
35554     },
35555
35556     sortAscText : "Sort Ascending",
35557     sortDescText : "Sort Descending",
35558     lockText : "Lock Column",
35559     unlockText : "Unlock Column",
35560     columnsText : "Columns"
35561 });
35562
35563
35564 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35565     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35566     this.proxy.el.addClass('x-grid3-col-dd');
35567 };
35568
35569 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35570     handleMouseDown : function(e){
35571
35572     },
35573
35574     callHandleMouseDown : function(e){
35575         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35576     }
35577 });
35578 /*
35579  * Based on:
35580  * Ext JS Library 1.1.1
35581  * Copyright(c) 2006-2007, Ext JS, LLC.
35582  *
35583  * Originally Released Under LGPL - original licence link has changed is not relivant.
35584  *
35585  * Fork - LGPL
35586  * <script type="text/javascript">
35587  */
35588  
35589 // private
35590 // This is a support class used internally by the Grid components
35591 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35592     this.grid = grid;
35593     this.view = grid.getView();
35594     this.proxy = this.view.resizeProxy;
35595     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35596         "gridSplitters" + this.grid.getGridEl().id, {
35597         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35598     });
35599     this.setHandleElId(Roo.id(hd));
35600     this.setOuterHandleElId(Roo.id(hd2));
35601     this.scroll = false;
35602 };
35603 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35604     fly: Roo.Element.fly,
35605
35606     b4StartDrag : function(x, y){
35607         this.view.headersDisabled = true;
35608         this.proxy.setHeight(this.view.mainWrap.getHeight());
35609         var w = this.cm.getColumnWidth(this.cellIndex);
35610         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35611         this.resetConstraints();
35612         this.setXConstraint(minw, 1000);
35613         this.setYConstraint(0, 0);
35614         this.minX = x - minw;
35615         this.maxX = x + 1000;
35616         this.startPos = x;
35617         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35618     },
35619
35620
35621     handleMouseDown : function(e){
35622         ev = Roo.EventObject.setEvent(e);
35623         var t = this.fly(ev.getTarget());
35624         if(t.hasClass("x-grid-split")){
35625             this.cellIndex = this.view.getCellIndex(t.dom);
35626             this.split = t.dom;
35627             this.cm = this.grid.colModel;
35628             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35629                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35630             }
35631         }
35632     },
35633
35634     endDrag : function(e){
35635         this.view.headersDisabled = false;
35636         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35637         var diff = endX - this.startPos;
35638         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35639     },
35640
35641     autoOffset : function(){
35642         this.setDelta(0,0);
35643     }
35644 });/*
35645  * Based on:
35646  * Ext JS Library 1.1.1
35647  * Copyright(c) 2006-2007, Ext JS, LLC.
35648  *
35649  * Originally Released Under LGPL - original licence link has changed is not relivant.
35650  *
35651  * Fork - LGPL
35652  * <script type="text/javascript">
35653  */
35654  
35655 // private
35656 // This is a support class used internally by the Grid components
35657 Roo.grid.GridDragZone = function(grid, config){
35658     this.view = grid.getView();
35659     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35660     if(this.view.lockedBody){
35661         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35662         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35663     }
35664     this.scroll = false;
35665     this.grid = grid;
35666     this.ddel = document.createElement('div');
35667     this.ddel.className = 'x-grid-dd-wrap';
35668 };
35669
35670 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35671     ddGroup : "GridDD",
35672
35673     getDragData : function(e){
35674         var t = Roo.lib.Event.getTarget(e);
35675         var rowIndex = this.view.findRowIndex(t);
35676         if(rowIndex !== false){
35677             var sm = this.grid.selModel;
35678             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35679               //  sm.mouseDown(e, t);
35680             //}
35681             if (e.hasModifier()){
35682                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35683             }
35684             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
35685         }
35686         return false;
35687     },
35688
35689     onInitDrag : function(e){
35690         var data = this.dragData;
35691         this.ddel.innerHTML = this.grid.getDragDropText();
35692         this.proxy.update(this.ddel);
35693         // fire start drag?
35694     },
35695
35696     afterRepair : function(){
35697         this.dragging = false;
35698     },
35699
35700     getRepairXY : function(e, data){
35701         return false;
35702     },
35703
35704     onEndDrag : function(data, e){
35705         // fire end drag?
35706     },
35707
35708     onValidDrop : function(dd, e, id){
35709         // fire drag drop?
35710         this.hideProxy();
35711     },
35712
35713     beforeInvalidDrop : function(e, id){
35714
35715     }
35716 });/*
35717  * Based on:
35718  * Ext JS Library 1.1.1
35719  * Copyright(c) 2006-2007, Ext JS, LLC.
35720  *
35721  * Originally Released Under LGPL - original licence link has changed is not relivant.
35722  *
35723  * Fork - LGPL
35724  * <script type="text/javascript">
35725  */
35726  
35727
35728 /**
35729  * @class Roo.grid.ColumnModel
35730  * @extends Roo.util.Observable
35731  * This is the default implementation of a ColumnModel used by the Grid. It defines
35732  * the columns in the grid.
35733  * <br>Usage:<br>
35734  <pre><code>
35735  var colModel = new Roo.grid.ColumnModel([
35736         {header: "Ticker", width: 60, sortable: true, locked: true},
35737         {header: "Company Name", width: 150, sortable: true},
35738         {header: "Market Cap.", width: 100, sortable: true},
35739         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35740         {header: "Employees", width: 100, sortable: true, resizable: false}
35741  ]);
35742  </code></pre>
35743  * <p>
35744  
35745  * The config options listed for this class are options which may appear in each
35746  * individual column definition.
35747  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35748  * @constructor
35749  * @param {Object} config An Array of column config objects. See this class's
35750  * config objects for details.
35751 */
35752 Roo.grid.ColumnModel = function(config){
35753         /**
35754      * The config passed into the constructor
35755      */
35756     this.config = config;
35757     this.lookup = {};
35758
35759     // if no id, create one
35760     // if the column does not have a dataIndex mapping,
35761     // map it to the order it is in the config
35762     for(var i = 0, len = config.length; i < len; i++){
35763         var c = config[i];
35764         if(typeof c.dataIndex == "undefined"){
35765             c.dataIndex = i;
35766         }
35767         if(typeof c.renderer == "string"){
35768             c.renderer = Roo.util.Format[c.renderer];
35769         }
35770         if(typeof c.id == "undefined"){
35771             c.id = Roo.id();
35772         }
35773         if(c.editor && c.editor.xtype){
35774             c.editor  = Roo.factory(c.editor, Roo.grid);
35775         }
35776         if(c.editor && c.editor.isFormField){
35777             c.editor = new Roo.grid.GridEditor(c.editor);
35778         }
35779         this.lookup[c.id] = c;
35780     }
35781
35782     /**
35783      * The width of columns which have no width specified (defaults to 100)
35784      * @type Number
35785      */
35786     this.defaultWidth = 100;
35787
35788     /**
35789      * Default sortable of columns which have no sortable specified (defaults to false)
35790      * @type Boolean
35791      */
35792     this.defaultSortable = false;
35793
35794     this.addEvents({
35795         /**
35796              * @event widthchange
35797              * Fires when the width of a column changes.
35798              * @param {ColumnModel} this
35799              * @param {Number} columnIndex The column index
35800              * @param {Number} newWidth The new width
35801              */
35802             "widthchange": true,
35803         /**
35804              * @event headerchange
35805              * Fires when the text of a header changes.
35806              * @param {ColumnModel} this
35807              * @param {Number} columnIndex The column index
35808              * @param {Number} newText The new header text
35809              */
35810             "headerchange": true,
35811         /**
35812              * @event hiddenchange
35813              * Fires when a column is hidden or "unhidden".
35814              * @param {ColumnModel} this
35815              * @param {Number} columnIndex The column index
35816              * @param {Boolean} hidden true if hidden, false otherwise
35817              */
35818             "hiddenchange": true,
35819             /**
35820          * @event columnmoved
35821          * Fires when a column is moved.
35822          * @param {ColumnModel} this
35823          * @param {Number} oldIndex
35824          * @param {Number} newIndex
35825          */
35826         "columnmoved" : true,
35827         /**
35828          * @event columlockchange
35829          * Fires when a column's locked state is changed
35830          * @param {ColumnModel} this
35831          * @param {Number} colIndex
35832          * @param {Boolean} locked true if locked
35833          */
35834         "columnlockchange" : true
35835     });
35836     Roo.grid.ColumnModel.superclass.constructor.call(this);
35837 };
35838 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35839     /**
35840      * @cfg {String} header The header text to display in the Grid view.
35841      */
35842     /**
35843      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35844      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35845      * specified, the column's index is used as an index into the Record's data Array.
35846      */
35847     /**
35848      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35849      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35850      */
35851     /**
35852      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35853      * Defaults to the value of the {@link #defaultSortable} property.
35854      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35855      */
35856     /**
35857      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35858      */
35859     /**
35860      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35861      */
35862     /**
35863      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35864      */
35865     /**
35866      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35867      */
35868     /**
35869      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35870      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35871      * default renderer uses the raw data value.
35872      */
35873        /**
35874      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35875      */
35876     /**
35877      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35878      */
35879
35880     /**
35881      * Returns the id of the column at the specified index.
35882      * @param {Number} index The column index
35883      * @return {String} the id
35884      */
35885     getColumnId : function(index){
35886         return this.config[index].id;
35887     },
35888
35889     /**
35890      * Returns the column for a specified id.
35891      * @param {String} id The column id
35892      * @return {Object} the column
35893      */
35894     getColumnById : function(id){
35895         return this.lookup[id];
35896     },
35897
35898     
35899     /**
35900      * Returns the column for a specified dataIndex.
35901      * @param {String} dataIndex The column dataIndex
35902      * @return {Object|Boolean} the column or false if not found
35903      */
35904     getColumnByDataIndex: function(dataIndex){
35905         var index = this.findColumnIndex(dataIndex);
35906         return index > -1 ? this.config[index] : false;
35907     },
35908     
35909     /**
35910      * Returns the index for a specified column id.
35911      * @param {String} id The column id
35912      * @return {Number} the index, or -1 if not found
35913      */
35914     getIndexById : function(id){
35915         for(var i = 0, len = this.config.length; i < len; i++){
35916             if(this.config[i].id == id){
35917                 return i;
35918             }
35919         }
35920         return -1;
35921     },
35922     
35923     /**
35924      * Returns the index for a specified column dataIndex.
35925      * @param {String} dataIndex The column dataIndex
35926      * @return {Number} the index, or -1 if not found
35927      */
35928     
35929     findColumnIndex : function(dataIndex){
35930         for(var i = 0, len = this.config.length; i < len; i++){
35931             if(this.config[i].dataIndex == dataIndex){
35932                 return i;
35933             }
35934         }
35935         return -1;
35936     },
35937     
35938     
35939     moveColumn : function(oldIndex, newIndex){
35940         var c = this.config[oldIndex];
35941         this.config.splice(oldIndex, 1);
35942         this.config.splice(newIndex, 0, c);
35943         this.dataMap = null;
35944         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35945     },
35946
35947     isLocked : function(colIndex){
35948         return this.config[colIndex].locked === true;
35949     },
35950
35951     setLocked : function(colIndex, value, suppressEvent){
35952         if(this.isLocked(colIndex) == value){
35953             return;
35954         }
35955         this.config[colIndex].locked = value;
35956         if(!suppressEvent){
35957             this.fireEvent("columnlockchange", this, colIndex, value);
35958         }
35959     },
35960
35961     getTotalLockedWidth : function(){
35962         var totalWidth = 0;
35963         for(var i = 0; i < this.config.length; i++){
35964             if(this.isLocked(i) && !this.isHidden(i)){
35965                 this.totalWidth += this.getColumnWidth(i);
35966             }
35967         }
35968         return totalWidth;
35969     },
35970
35971     getLockedCount : function(){
35972         for(var i = 0, len = this.config.length; i < len; i++){
35973             if(!this.isLocked(i)){
35974                 return i;
35975             }
35976         }
35977     },
35978
35979     /**
35980      * Returns the number of columns.
35981      * @return {Number}
35982      */
35983     getColumnCount : function(visibleOnly){
35984         if(visibleOnly === true){
35985             var c = 0;
35986             for(var i = 0, len = this.config.length; i < len; i++){
35987                 if(!this.isHidden(i)){
35988                     c++;
35989                 }
35990             }
35991             return c;
35992         }
35993         return this.config.length;
35994     },
35995
35996     /**
35997      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35998      * @param {Function} fn
35999      * @param {Object} scope (optional)
36000      * @return {Array} result
36001      */
36002     getColumnsBy : function(fn, scope){
36003         var r = [];
36004         for(var i = 0, len = this.config.length; i < len; i++){
36005             var c = this.config[i];
36006             if(fn.call(scope||this, c, i) === true){
36007                 r[r.length] = c;
36008             }
36009         }
36010         return r;
36011     },
36012
36013     /**
36014      * Returns true if the specified column is sortable.
36015      * @param {Number} col The column index
36016      * @return {Boolean}
36017      */
36018     isSortable : function(col){
36019         if(typeof this.config[col].sortable == "undefined"){
36020             return this.defaultSortable;
36021         }
36022         return this.config[col].sortable;
36023     },
36024
36025     /**
36026      * Returns the rendering (formatting) function defined for the column.
36027      * @param {Number} col The column index.
36028      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36029      */
36030     getRenderer : function(col){
36031         if(!this.config[col].renderer){
36032             return Roo.grid.ColumnModel.defaultRenderer;
36033         }
36034         return this.config[col].renderer;
36035     },
36036
36037     /**
36038      * Sets the rendering (formatting) function for a column.
36039      * @param {Number} col The column index
36040      * @param {Function} fn The function to use to process the cell's raw data
36041      * to return HTML markup for the grid view. The render function is called with
36042      * the following parameters:<ul>
36043      * <li>Data value.</li>
36044      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36045      * <li>css A CSS style string to apply to the table cell.</li>
36046      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36047      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36048      * <li>Row index</li>
36049      * <li>Column index</li>
36050      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36051      */
36052     setRenderer : function(col, fn){
36053         this.config[col].renderer = fn;
36054     },
36055
36056     /**
36057      * Returns the width for the specified column.
36058      * @param {Number} col The column index
36059      * @return {Number}
36060      */
36061     getColumnWidth : function(col){
36062         return this.config[col].width * 1 || this.defaultWidth;
36063     },
36064
36065     /**
36066      * Sets the width for a column.
36067      * @param {Number} col The column index
36068      * @param {Number} width The new width
36069      */
36070     setColumnWidth : function(col, width, suppressEvent){
36071         this.config[col].width = width;
36072         this.totalWidth = null;
36073         if(!suppressEvent){
36074              this.fireEvent("widthchange", this, col, width);
36075         }
36076     },
36077
36078     /**
36079      * Returns the total width of all columns.
36080      * @param {Boolean} includeHidden True to include hidden column widths
36081      * @return {Number}
36082      */
36083     getTotalWidth : function(includeHidden){
36084         if(!this.totalWidth){
36085             this.totalWidth = 0;
36086             for(var i = 0, len = this.config.length; i < len; i++){
36087                 if(includeHidden || !this.isHidden(i)){
36088                     this.totalWidth += this.getColumnWidth(i);
36089                 }
36090             }
36091         }
36092         return this.totalWidth;
36093     },
36094
36095     /**
36096      * Returns the header for the specified column.
36097      * @param {Number} col The column index
36098      * @return {String}
36099      */
36100     getColumnHeader : function(col){
36101         return this.config[col].header;
36102     },
36103
36104     /**
36105      * Sets the header for a column.
36106      * @param {Number} col The column index
36107      * @param {String} header The new header
36108      */
36109     setColumnHeader : function(col, header){
36110         this.config[col].header = header;
36111         this.fireEvent("headerchange", this, col, header);
36112     },
36113
36114     /**
36115      * Returns the tooltip for the specified column.
36116      * @param {Number} col The column index
36117      * @return {String}
36118      */
36119     getColumnTooltip : function(col){
36120             return this.config[col].tooltip;
36121     },
36122     /**
36123      * Sets the tooltip for a column.
36124      * @param {Number} col The column index
36125      * @param {String} tooltip The new tooltip
36126      */
36127     setColumnTooltip : function(col, tooltip){
36128             this.config[col].tooltip = tooltip;
36129     },
36130
36131     /**
36132      * Returns the dataIndex for the specified column.
36133      * @param {Number} col The column index
36134      * @return {Number}
36135      */
36136     getDataIndex : function(col){
36137         return this.config[col].dataIndex;
36138     },
36139
36140     /**
36141      * Sets the dataIndex for a column.
36142      * @param {Number} col The column index
36143      * @param {Number} dataIndex The new dataIndex
36144      */
36145     setDataIndex : function(col, dataIndex){
36146         this.config[col].dataIndex = dataIndex;
36147     },
36148
36149     
36150     
36151     /**
36152      * Returns true if the cell is editable.
36153      * @param {Number} colIndex The column index
36154      * @param {Number} rowIndex The row index
36155      * @return {Boolean}
36156      */
36157     isCellEditable : function(colIndex, rowIndex){
36158         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36159     },
36160
36161     /**
36162      * Returns the editor defined for the cell/column.
36163      * return false or null to disable editing.
36164      * @param {Number} colIndex The column index
36165      * @param {Number} rowIndex The row index
36166      * @return {Object}
36167      */
36168     getCellEditor : function(colIndex, rowIndex){
36169         return this.config[colIndex].editor;
36170     },
36171
36172     /**
36173      * Sets if a column is editable.
36174      * @param {Number} col The column index
36175      * @param {Boolean} editable True if the column is editable
36176      */
36177     setEditable : function(col, editable){
36178         this.config[col].editable = editable;
36179     },
36180
36181
36182     /**
36183      * Returns true if the column is hidden.
36184      * @param {Number} colIndex The column index
36185      * @return {Boolean}
36186      */
36187     isHidden : function(colIndex){
36188         return this.config[colIndex].hidden;
36189     },
36190
36191
36192     /**
36193      * Returns true if the column width cannot be changed
36194      */
36195     isFixed : function(colIndex){
36196         return this.config[colIndex].fixed;
36197     },
36198
36199     /**
36200      * Returns true if the column can be resized
36201      * @return {Boolean}
36202      */
36203     isResizable : function(colIndex){
36204         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36205     },
36206     /**
36207      * Sets if a column is hidden.
36208      * @param {Number} colIndex The column index
36209      * @param {Boolean} hidden True if the column is hidden
36210      */
36211     setHidden : function(colIndex, hidden){
36212         this.config[colIndex].hidden = hidden;
36213         this.totalWidth = null;
36214         this.fireEvent("hiddenchange", this, colIndex, hidden);
36215     },
36216
36217     /**
36218      * Sets the editor for a column.
36219      * @param {Number} col The column index
36220      * @param {Object} editor The editor object
36221      */
36222     setEditor : function(col, editor){
36223         this.config[col].editor = editor;
36224     }
36225 });
36226
36227 Roo.grid.ColumnModel.defaultRenderer = function(value){
36228         if(typeof value == "string" && value.length < 1){
36229             return "&#160;";
36230         }
36231         return value;
36232 };
36233
36234 // Alias for backwards compatibility
36235 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36236 /*
36237  * Based on:
36238  * Ext JS Library 1.1.1
36239  * Copyright(c) 2006-2007, Ext JS, LLC.
36240  *
36241  * Originally Released Under LGPL - original licence link has changed is not relivant.
36242  *
36243  * Fork - LGPL
36244  * <script type="text/javascript">
36245  */
36246
36247 /**
36248  * @class Roo.grid.AbstractSelectionModel
36249  * @extends Roo.util.Observable
36250  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36251  * implemented by descendant classes.  This class should not be directly instantiated.
36252  * @constructor
36253  */
36254 Roo.grid.AbstractSelectionModel = function(){
36255     this.locked = false;
36256     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36257 };
36258
36259 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36260     /** @ignore Called by the grid automatically. Do not call directly. */
36261     init : function(grid){
36262         this.grid = grid;
36263         this.initEvents();
36264     },
36265
36266     /**
36267      * Locks the selections.
36268      */
36269     lock : function(){
36270         this.locked = true;
36271     },
36272
36273     /**
36274      * Unlocks the selections.
36275      */
36276     unlock : function(){
36277         this.locked = false;
36278     },
36279
36280     /**
36281      * Returns true if the selections are locked.
36282      * @return {Boolean}
36283      */
36284     isLocked : function(){
36285         return this.locked;
36286     }
36287 });/*
36288  * Based on:
36289  * Ext JS Library 1.1.1
36290  * Copyright(c) 2006-2007, Ext JS, LLC.
36291  *
36292  * Originally Released Under LGPL - original licence link has changed is not relivant.
36293  *
36294  * Fork - LGPL
36295  * <script type="text/javascript">
36296  */
36297 /**
36298  * @extends Roo.grid.AbstractSelectionModel
36299  * @class Roo.grid.RowSelectionModel
36300  * The default SelectionModel used by {@link Roo.grid.Grid}.
36301  * It supports multiple selections and keyboard selection/navigation. 
36302  * @constructor
36303  * @param {Object} config
36304  */
36305 Roo.grid.RowSelectionModel = function(config){
36306     Roo.apply(this, config);
36307     this.selections = new Roo.util.MixedCollection(false, function(o){
36308         return o.id;
36309     });
36310
36311     this.last = false;
36312     this.lastActive = false;
36313
36314     this.addEvents({
36315         /**
36316              * @event selectionchange
36317              * Fires when the selection changes
36318              * @param {SelectionModel} this
36319              */
36320             "selectionchange" : true,
36321         /**
36322              * @event afterselectionchange
36323              * Fires after the selection changes (eg. by key press or clicking)
36324              * @param {SelectionModel} this
36325              */
36326             "afterselectionchange" : true,
36327         /**
36328              * @event beforerowselect
36329              * Fires when a row is selected being selected, return false to cancel.
36330              * @param {SelectionModel} this
36331              * @param {Number} rowIndex The selected index
36332              * @param {Boolean} keepExisting False if other selections will be cleared
36333              */
36334             "beforerowselect" : true,
36335         /**
36336              * @event rowselect
36337              * Fires when a row is selected.
36338              * @param {SelectionModel} this
36339              * @param {Number} rowIndex The selected index
36340              * @param {Roo.data.Record} r The record
36341              */
36342             "rowselect" : true,
36343         /**
36344              * @event rowdeselect
36345              * Fires when a row is deselected.
36346              * @param {SelectionModel} this
36347              * @param {Number} rowIndex The selected index
36348              */
36349         "rowdeselect" : true
36350     });
36351     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36352     this.locked = false;
36353 };
36354
36355 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36356     /**
36357      * @cfg {Boolean} singleSelect
36358      * True to allow selection of only one row at a time (defaults to false)
36359      */
36360     singleSelect : false,
36361
36362     // private
36363     initEvents : function(){
36364
36365         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36366             this.grid.on("mousedown", this.handleMouseDown, this);
36367         }else{ // allow click to work like normal
36368             this.grid.on("rowclick", this.handleDragableRowClick, this);
36369         }
36370
36371         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36372             "up" : function(e){
36373                 if(!e.shiftKey){
36374                     this.selectPrevious(e.shiftKey);
36375                 }else if(this.last !== false && this.lastActive !== false){
36376                     var last = this.last;
36377                     this.selectRange(this.last,  this.lastActive-1);
36378                     this.grid.getView().focusRow(this.lastActive);
36379                     if(last !== false){
36380                         this.last = last;
36381                     }
36382                 }else{
36383                     this.selectFirstRow();
36384                 }
36385                 this.fireEvent("afterselectionchange", this);
36386             },
36387             "down" : function(e){
36388                 if(!e.shiftKey){
36389                     this.selectNext(e.shiftKey);
36390                 }else if(this.last !== false && this.lastActive !== false){
36391                     var last = this.last;
36392                     this.selectRange(this.last,  this.lastActive+1);
36393                     this.grid.getView().focusRow(this.lastActive);
36394                     if(last !== false){
36395                         this.last = last;
36396                     }
36397                 }else{
36398                     this.selectFirstRow();
36399                 }
36400                 this.fireEvent("afterselectionchange", this);
36401             },
36402             scope: this
36403         });
36404
36405         var view = this.grid.view;
36406         view.on("refresh", this.onRefresh, this);
36407         view.on("rowupdated", this.onRowUpdated, this);
36408         view.on("rowremoved", this.onRemove, this);
36409     },
36410
36411     // private
36412     onRefresh : function(){
36413         var ds = this.grid.dataSource, i, v = this.grid.view;
36414         var s = this.selections;
36415         s.each(function(r){
36416             if((i = ds.indexOfId(r.id)) != -1){
36417                 v.onRowSelect(i);
36418             }else{
36419                 s.remove(r);
36420             }
36421         });
36422     },
36423
36424     // private
36425     onRemove : function(v, index, r){
36426         this.selections.remove(r);
36427     },
36428
36429     // private
36430     onRowUpdated : function(v, index, r){
36431         if(this.isSelected(r)){
36432             v.onRowSelect(index);
36433         }
36434     },
36435
36436     /**
36437      * Select records.
36438      * @param {Array} records The records to select
36439      * @param {Boolean} keepExisting (optional) True to keep existing selections
36440      */
36441     selectRecords : function(records, keepExisting){
36442         if(!keepExisting){
36443             this.clearSelections();
36444         }
36445         var ds = this.grid.dataSource;
36446         for(var i = 0, len = records.length; i < len; i++){
36447             this.selectRow(ds.indexOf(records[i]), true);
36448         }
36449     },
36450
36451     /**
36452      * Gets the number of selected rows.
36453      * @return {Number}
36454      */
36455     getCount : function(){
36456         return this.selections.length;
36457     },
36458
36459     /**
36460      * Selects the first row in the grid.
36461      */
36462     selectFirstRow : function(){
36463         this.selectRow(0);
36464     },
36465
36466     /**
36467      * Select the last row.
36468      * @param {Boolean} keepExisting (optional) True to keep existing selections
36469      */
36470     selectLastRow : function(keepExisting){
36471         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36472     },
36473
36474     /**
36475      * Selects the row immediately following the last selected row.
36476      * @param {Boolean} keepExisting (optional) True to keep existing selections
36477      */
36478     selectNext : function(keepExisting){
36479         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36480             this.selectRow(this.last+1, keepExisting);
36481             this.grid.getView().focusRow(this.last);
36482         }
36483     },
36484
36485     /**
36486      * Selects the row that precedes the last selected row.
36487      * @param {Boolean} keepExisting (optional) True to keep existing selections
36488      */
36489     selectPrevious : function(keepExisting){
36490         if(this.last){
36491             this.selectRow(this.last-1, keepExisting);
36492             this.grid.getView().focusRow(this.last);
36493         }
36494     },
36495
36496     /**
36497      * Returns the selected records
36498      * @return {Array} Array of selected records
36499      */
36500     getSelections : function(){
36501         return [].concat(this.selections.items);
36502     },
36503
36504     /**
36505      * Returns the first selected record.
36506      * @return {Record}
36507      */
36508     getSelected : function(){
36509         return this.selections.itemAt(0);
36510     },
36511
36512
36513     /**
36514      * Clears all selections.
36515      */
36516     clearSelections : function(fast){
36517         if(this.locked) return;
36518         if(fast !== true){
36519             var ds = this.grid.dataSource;
36520             var s = this.selections;
36521             s.each(function(r){
36522                 this.deselectRow(ds.indexOfId(r.id));
36523             }, this);
36524             s.clear();
36525         }else{
36526             this.selections.clear();
36527         }
36528         this.last = false;
36529     },
36530
36531
36532     /**
36533      * Selects all rows.
36534      */
36535     selectAll : function(){
36536         if(this.locked) return;
36537         this.selections.clear();
36538         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36539             this.selectRow(i, true);
36540         }
36541     },
36542
36543     /**
36544      * Returns True if there is a selection.
36545      * @return {Boolean}
36546      */
36547     hasSelection : function(){
36548         return this.selections.length > 0;
36549     },
36550
36551     /**
36552      * Returns True if the specified row is selected.
36553      * @param {Number/Record} record The record or index of the record to check
36554      * @return {Boolean}
36555      */
36556     isSelected : function(index){
36557         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36558         return (r && this.selections.key(r.id) ? true : false);
36559     },
36560
36561     /**
36562      * Returns True if the specified record id is selected.
36563      * @param {String} id The id of record to check
36564      * @return {Boolean}
36565      */
36566     isIdSelected : function(id){
36567         return (this.selections.key(id) ? true : false);
36568     },
36569
36570     // private
36571     handleMouseDown : function(e, t){
36572         var view = this.grid.getView(), rowIndex;
36573         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36574             return;
36575         };
36576         if(e.shiftKey && this.last !== false){
36577             var last = this.last;
36578             this.selectRange(last, rowIndex, e.ctrlKey);
36579             this.last = last; // reset the last
36580             view.focusRow(rowIndex);
36581         }else{
36582             var isSelected = this.isSelected(rowIndex);
36583             if(e.button !== 0 && isSelected){
36584                 view.focusRow(rowIndex);
36585             }else if(e.ctrlKey && isSelected){
36586                 this.deselectRow(rowIndex);
36587             }else if(!isSelected){
36588                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36589                 view.focusRow(rowIndex);
36590             }
36591         }
36592         this.fireEvent("afterselectionchange", this);
36593     },
36594     // private
36595     handleDragableRowClick :  function(grid, rowIndex, e) 
36596     {
36597         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36598             this.selectRow(rowIndex, false);
36599             grid.view.focusRow(rowIndex);
36600              this.fireEvent("afterselectionchange", this);
36601         }
36602     },
36603     
36604     /**
36605      * Selects multiple rows.
36606      * @param {Array} rows Array of the indexes of the row to select
36607      * @param {Boolean} keepExisting (optional) True to keep existing selections
36608      */
36609     selectRows : function(rows, keepExisting){
36610         if(!keepExisting){
36611             this.clearSelections();
36612         }
36613         for(var i = 0, len = rows.length; i < len; i++){
36614             this.selectRow(rows[i], true);
36615         }
36616     },
36617
36618     /**
36619      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36620      * @param {Number} startRow The index of the first row in the range
36621      * @param {Number} endRow The index of the last row in the range
36622      * @param {Boolean} keepExisting (optional) True to retain existing selections
36623      */
36624     selectRange : function(startRow, endRow, keepExisting){
36625         if(this.locked) return;
36626         if(!keepExisting){
36627             this.clearSelections();
36628         }
36629         if(startRow <= endRow){
36630             for(var i = startRow; i <= endRow; i++){
36631                 this.selectRow(i, true);
36632             }
36633         }else{
36634             for(var i = startRow; i >= endRow; i--){
36635                 this.selectRow(i, true);
36636             }
36637         }
36638     },
36639
36640     /**
36641      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36642      * @param {Number} startRow The index of the first row in the range
36643      * @param {Number} endRow The index of the last row in the range
36644      */
36645     deselectRange : function(startRow, endRow, preventViewNotify){
36646         if(this.locked) return;
36647         for(var i = startRow; i <= endRow; i++){
36648             this.deselectRow(i, preventViewNotify);
36649         }
36650     },
36651
36652     /**
36653      * Selects a row.
36654      * @param {Number} row The index of the row to select
36655      * @param {Boolean} keepExisting (optional) True to keep existing selections
36656      */
36657     selectRow : function(index, keepExisting, preventViewNotify){
36658         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
36659         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36660             if(!keepExisting || this.singleSelect){
36661                 this.clearSelections();
36662             }
36663             var r = this.grid.dataSource.getAt(index);
36664             this.selections.add(r);
36665             this.last = this.lastActive = index;
36666             if(!preventViewNotify){
36667                 this.grid.getView().onRowSelect(index);
36668             }
36669             this.fireEvent("rowselect", this, index, r);
36670             this.fireEvent("selectionchange", this);
36671         }
36672     },
36673
36674     /**
36675      * Deselects a row.
36676      * @param {Number} row The index of the row to deselect
36677      */
36678     deselectRow : function(index, preventViewNotify){
36679         if(this.locked) return;
36680         if(this.last == index){
36681             this.last = false;
36682         }
36683         if(this.lastActive == index){
36684             this.lastActive = false;
36685         }
36686         var r = this.grid.dataSource.getAt(index);
36687         this.selections.remove(r);
36688         if(!preventViewNotify){
36689             this.grid.getView().onRowDeselect(index);
36690         }
36691         this.fireEvent("rowdeselect", this, index);
36692         this.fireEvent("selectionchange", this);
36693     },
36694
36695     // private
36696     restoreLast : function(){
36697         if(this._last){
36698             this.last = this._last;
36699         }
36700     },
36701
36702     // private
36703     acceptsNav : function(row, col, cm){
36704         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36705     },
36706
36707     // private
36708     onEditorKey : function(field, e){
36709         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36710         if(k == e.TAB){
36711             e.stopEvent();
36712             ed.completeEdit();
36713             if(e.shiftKey){
36714                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36715             }else{
36716                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36717             }
36718         }else if(k == e.ENTER && !e.ctrlKey){
36719             e.stopEvent();
36720             ed.completeEdit();
36721             if(e.shiftKey){
36722                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36723             }else{
36724                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36725             }
36726         }else if(k == e.ESC){
36727             ed.cancelEdit();
36728         }
36729         if(newCell){
36730             g.startEditing(newCell[0], newCell[1]);
36731         }
36732     }
36733 });/*
36734  * Based on:
36735  * Ext JS Library 1.1.1
36736  * Copyright(c) 2006-2007, Ext JS, LLC.
36737  *
36738  * Originally Released Under LGPL - original licence link has changed is not relivant.
36739  *
36740  * Fork - LGPL
36741  * <script type="text/javascript">
36742  */
36743 /**
36744  * @class Roo.grid.CellSelectionModel
36745  * @extends Roo.grid.AbstractSelectionModel
36746  * This class provides the basic implementation for cell selection in a grid.
36747  * @constructor
36748  * @param {Object} config The object containing the configuration of this model.
36749  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36750  */
36751 Roo.grid.CellSelectionModel = function(config){
36752     Roo.apply(this, config);
36753
36754     this.selection = null;
36755
36756     this.addEvents({
36757         /**
36758              * @event beforerowselect
36759              * Fires before a cell is selected.
36760              * @param {SelectionModel} this
36761              * @param {Number} rowIndex The selected row index
36762              * @param {Number} colIndex The selected cell index
36763              */
36764             "beforecellselect" : true,
36765         /**
36766              * @event cellselect
36767              * Fires when a cell is selected.
36768              * @param {SelectionModel} this
36769              * @param {Number} rowIndex The selected row index
36770              * @param {Number} colIndex The selected cell index
36771              */
36772             "cellselect" : true,
36773         /**
36774              * @event selectionchange
36775              * Fires when the active selection changes.
36776              * @param {SelectionModel} this
36777              * @param {Object} selection null for no selection or an object (o) with two properties
36778                 <ul>
36779                 <li>o.record: the record object for the row the selection is in</li>
36780                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36781                 </ul>
36782              */
36783             "selectionchange" : true,
36784         /**
36785              * @event tabend
36786              * Fires when the tab (or enter) was pressed on the last editable cell
36787              * You can use this to trigger add new row.
36788              * @param {SelectionModel} this
36789              */
36790             "tabend" : true
36791     });
36792     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36793 };
36794
36795 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36796     
36797     enter_is_tab: false,
36798
36799     /** @ignore */
36800     initEvents : function(){
36801         this.grid.on("mousedown", this.handleMouseDown, this);
36802         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36803         var view = this.grid.view;
36804         view.on("refresh", this.onViewChange, this);
36805         view.on("rowupdated", this.onRowUpdated, this);
36806         view.on("beforerowremoved", this.clearSelections, this);
36807         view.on("beforerowsinserted", this.clearSelections, this);
36808         if(this.grid.isEditor){
36809             this.grid.on("beforeedit", this.beforeEdit,  this);
36810         }
36811     },
36812
36813         //private
36814     beforeEdit : function(e){
36815         this.select(e.row, e.column, false, true, e.record);
36816     },
36817
36818         //private
36819     onRowUpdated : function(v, index, r){
36820         if(this.selection && this.selection.record == r){
36821             v.onCellSelect(index, this.selection.cell[1]);
36822         }
36823     },
36824
36825         //private
36826     onViewChange : function(){
36827         this.clearSelections(true);
36828     },
36829
36830         /**
36831          * Returns the currently selected cell,.
36832          * @return {Array} The selected cell (row, column) or null if none selected.
36833          */
36834     getSelectedCell : function(){
36835         return this.selection ? this.selection.cell : null;
36836     },
36837
36838     /**
36839      * Clears all selections.
36840      * @param {Boolean} true to prevent the gridview from being notified about the change.
36841      */
36842     clearSelections : function(preventNotify){
36843         var s = this.selection;
36844         if(s){
36845             if(preventNotify !== true){
36846                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36847             }
36848             this.selection = null;
36849             this.fireEvent("selectionchange", this, null);
36850         }
36851     },
36852
36853     /**
36854      * Returns true if there is a selection.
36855      * @return {Boolean}
36856      */
36857     hasSelection : function(){
36858         return this.selection ? true : false;
36859     },
36860
36861     /** @ignore */
36862     handleMouseDown : function(e, t){
36863         var v = this.grid.getView();
36864         if(this.isLocked()){
36865             return;
36866         };
36867         var row = v.findRowIndex(t);
36868         var cell = v.findCellIndex(t);
36869         if(row !== false && cell !== false){
36870             this.select(row, cell);
36871         }
36872     },
36873
36874     /**
36875      * Selects a cell.
36876      * @param {Number} rowIndex
36877      * @param {Number} collIndex
36878      */
36879     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36880         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36881             this.clearSelections();
36882             r = r || this.grid.dataSource.getAt(rowIndex);
36883             this.selection = {
36884                 record : r,
36885                 cell : [rowIndex, colIndex]
36886             };
36887             if(!preventViewNotify){
36888                 var v = this.grid.getView();
36889                 v.onCellSelect(rowIndex, colIndex);
36890                 if(preventFocus !== true){
36891                     v.focusCell(rowIndex, colIndex);
36892                 }
36893             }
36894             this.fireEvent("cellselect", this, rowIndex, colIndex);
36895             this.fireEvent("selectionchange", this, this.selection);
36896         }
36897     },
36898
36899         //private
36900     isSelectable : function(rowIndex, colIndex, cm){
36901         return !cm.isHidden(colIndex);
36902     },
36903
36904     /** @ignore */
36905     handleKeyDown : function(e){
36906         //Roo.log('Cell Sel Model handleKeyDown');
36907         if(!e.isNavKeyPress()){
36908             return;
36909         }
36910         var g = this.grid, s = this.selection;
36911         if(!s){
36912             e.stopEvent();
36913             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36914             if(cell){
36915                 this.select(cell[0], cell[1]);
36916             }
36917             return;
36918         }
36919         var sm = this;
36920         var walk = function(row, col, step){
36921             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36922         };
36923         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36924         var newCell;
36925
36926       
36927
36928         switch(k){
36929             case e.TAB:
36930                 // handled by onEditorKey
36931                 if (g.isEditor && g.editing) {
36932                     return;
36933                 }
36934                 if(e.shiftKey) {
36935                     newCell = walk(r, c-1, -1);
36936                 } else {
36937                     newCell = walk(r, c+1, 1);
36938                 }
36939                 break;
36940             
36941             case e.DOWN:
36942                newCell = walk(r+1, c, 1);
36943                 break;
36944             
36945             case e.UP:
36946                 newCell = walk(r-1, c, -1);
36947                 break;
36948             
36949             case e.RIGHT:
36950                 newCell = walk(r, c+1, 1);
36951                 break;
36952             
36953             case e.LEFT:
36954                 newCell = walk(r, c-1, -1);
36955                 break;
36956             
36957             case e.ENTER:
36958                 
36959                 if(g.isEditor && !g.editing){
36960                    g.startEditing(r, c);
36961                    e.stopEvent();
36962                    return;
36963                 }
36964                 
36965                 
36966              break;
36967         };
36968         if(newCell){
36969             this.select(newCell[0], newCell[1]);
36970             e.stopEvent();
36971             
36972         }
36973     },
36974
36975     acceptsNav : function(row, col, cm){
36976         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36977     },
36978     /**
36979      * Selects a cell.
36980      * @param {Number} field (not used) - as it's normally used as a listener
36981      * @param {Number} e - event - fake it by using
36982      *
36983      * var e = Roo.EventObjectImpl.prototype;
36984      * e.keyCode = e.TAB
36985      *
36986      * 
36987      */
36988     onEditorKey : function(field, e){
36989         
36990         var k = e.getKey(),
36991             newCell,
36992             g = this.grid,
36993             ed = g.activeEditor,
36994             forward = false;
36995         ///Roo.log('onEditorKey' + k);
36996         
36997         
36998         if (this.enter_is_tab && k == e.ENTER) {
36999             k = e.TAB;
37000         }
37001         
37002         if(k == e.TAB){
37003             if(e.shiftKey){
37004                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37005             }else{
37006                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37007                 forward = true;
37008             }
37009             
37010             e.stopEvent();
37011             
37012         }else if(k == e.ENTER &&  !e.ctrlKey){
37013             ed.completeEdit();
37014             e.stopEvent();
37015             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37016         }else if(k == e.ESC){
37017             ed.cancelEdit();
37018         }
37019         
37020         
37021         if(newCell){
37022             //Roo.log('next cell after edit');
37023             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37024         } else if (forward) {
37025             // tabbed past last
37026             this.fireEvent.defer(100, this, ['tabend',this]);
37027         }
37028     }
37029 });/*
37030  * Based on:
37031  * Ext JS Library 1.1.1
37032  * Copyright(c) 2006-2007, Ext JS, LLC.
37033  *
37034  * Originally Released Under LGPL - original licence link has changed is not relivant.
37035  *
37036  * Fork - LGPL
37037  * <script type="text/javascript">
37038  */
37039  
37040 /**
37041  * @class Roo.grid.EditorGrid
37042  * @extends Roo.grid.Grid
37043  * Class for creating and editable grid.
37044  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37045  * The container MUST have some type of size defined for the grid to fill. The container will be 
37046  * automatically set to position relative if it isn't already.
37047  * @param {Object} dataSource The data model to bind to
37048  * @param {Object} colModel The column model with info about this grid's columns
37049  */
37050 Roo.grid.EditorGrid = function(container, config){
37051     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37052     this.getGridEl().addClass("xedit-grid");
37053
37054     if(!this.selModel){
37055         this.selModel = new Roo.grid.CellSelectionModel();
37056     }
37057
37058     this.activeEditor = null;
37059
37060         this.addEvents({
37061             /**
37062              * @event beforeedit
37063              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37064              * <ul style="padding:5px;padding-left:16px;">
37065              * <li>grid - This grid</li>
37066              * <li>record - The record being edited</li>
37067              * <li>field - The field name being edited</li>
37068              * <li>value - The value for the field being edited.</li>
37069              * <li>row - The grid row index</li>
37070              * <li>column - The grid column index</li>
37071              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37072              * </ul>
37073              * @param {Object} e An edit event (see above for description)
37074              */
37075             "beforeedit" : true,
37076             /**
37077              * @event afteredit
37078              * Fires after a cell is edited. <br />
37079              * <ul style="padding:5px;padding-left:16px;">
37080              * <li>grid - This grid</li>
37081              * <li>record - The record being edited</li>
37082              * <li>field - The field name being edited</li>
37083              * <li>value - The value being set</li>
37084              * <li>originalValue - The original value for the field, before the edit.</li>
37085              * <li>row - The grid row index</li>
37086              * <li>column - The grid column index</li>
37087              * </ul>
37088              * @param {Object} e An edit event (see above for description)
37089              */
37090             "afteredit" : true,
37091             /**
37092              * @event validateedit
37093              * Fires after a cell is edited, but before the value is set in the record. 
37094          * You can use this to modify the value being set in the field, Return false
37095              * to cancel the change. The edit event object has the following properties <br />
37096              * <ul style="padding:5px;padding-left:16px;">
37097          * <li>editor - This editor</li>
37098              * <li>grid - This grid</li>
37099              * <li>record - The record being edited</li>
37100              * <li>field - The field name being edited</li>
37101              * <li>value - The value being set</li>
37102              * <li>originalValue - The original value for the field, before the edit.</li>
37103              * <li>row - The grid row index</li>
37104              * <li>column - The grid column index</li>
37105              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37106              * </ul>
37107              * @param {Object} e An edit event (see above for description)
37108              */
37109             "validateedit" : true
37110         });
37111     this.on("bodyscroll", this.stopEditing,  this);
37112     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37113 };
37114
37115 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37116     /**
37117      * @cfg {Number} clicksToEdit
37118      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37119      */
37120     clicksToEdit: 2,
37121
37122     // private
37123     isEditor : true,
37124     // private
37125     trackMouseOver: false, // causes very odd FF errors
37126
37127     onCellDblClick : function(g, row, col){
37128         this.startEditing(row, col);
37129     },
37130
37131     onEditComplete : function(ed, value, startValue){
37132         this.editing = false;
37133         this.activeEditor = null;
37134         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37135         var r = ed.record;
37136         var field = this.colModel.getDataIndex(ed.col);
37137         var e = {
37138             grid: this,
37139             record: r,
37140             field: field,
37141             originalValue: startValue,
37142             value: value,
37143             row: ed.row,
37144             column: ed.col,
37145             cancel:false,
37146             editor: ed
37147         };
37148         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37149         cell.show();
37150           
37151         if(String(value) !== String(startValue)){
37152             
37153             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37154                 r.set(field, e.value);
37155                 // if we are dealing with a combo box..
37156                 // then we also set the 'name' colum to be the displayField
37157                 if (ed.field.displayField && ed.field.name) {
37158                     r.set(ed.field.name, ed.field.el.dom.value);
37159                 }
37160                 
37161                 delete e.cancel; //?? why!!!
37162                 this.fireEvent("afteredit", e);
37163             }
37164         } else {
37165             this.fireEvent("afteredit", e); // always fire it!
37166         }
37167         this.view.focusCell(ed.row, ed.col);
37168     },
37169
37170     /**
37171      * Starts editing the specified for the specified row/column
37172      * @param {Number} rowIndex
37173      * @param {Number} colIndex
37174      */
37175     startEditing : function(row, col){
37176         this.stopEditing();
37177         if(this.colModel.isCellEditable(col, row)){
37178             this.view.ensureVisible(row, col, true);
37179           
37180             var r = this.dataSource.getAt(row);
37181             var field = this.colModel.getDataIndex(col);
37182             var cell = Roo.get(this.view.getCell(row,col));
37183             var e = {
37184                 grid: this,
37185                 record: r,
37186                 field: field,
37187                 value: r.data[field],
37188                 row: row,
37189                 column: col,
37190                 cancel:false 
37191             };
37192             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37193                 this.editing = true;
37194                 var ed = this.colModel.getCellEditor(col, row);
37195                 
37196                 if (!ed) {
37197                     return;
37198                 }
37199                 if(!ed.rendered){
37200                     ed.render(ed.parentEl || document.body);
37201                 }
37202                 ed.field.reset();
37203                
37204                 cell.hide();
37205                 
37206                 (function(){ // complex but required for focus issues in safari, ie and opera
37207                     ed.row = row;
37208                     ed.col = col;
37209                     ed.record = r;
37210                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37211                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37212                     this.activeEditor = ed;
37213                     var v = r.data[field];
37214                     ed.startEdit(this.view.getCell(row, col), v);
37215                     // combo's with 'displayField and name set
37216                     if (ed.field.displayField && ed.field.name) {
37217                         ed.field.el.dom.value = r.data[ed.field.name];
37218                     }
37219                     
37220                     
37221                 }).defer(50, this);
37222             }
37223         }
37224     },
37225         
37226     /**
37227      * Stops any active editing
37228      */
37229     stopEditing : function(){
37230         if(this.activeEditor){
37231             this.activeEditor.completeEdit();
37232         }
37233         this.activeEditor = null;
37234     }
37235 });/*
37236  * Based on:
37237  * Ext JS Library 1.1.1
37238  * Copyright(c) 2006-2007, Ext JS, LLC.
37239  *
37240  * Originally Released Under LGPL - original licence link has changed is not relivant.
37241  *
37242  * Fork - LGPL
37243  * <script type="text/javascript">
37244  */
37245
37246 // private - not really -- you end up using it !
37247 // This is a support class used internally by the Grid components
37248
37249 /**
37250  * @class Roo.grid.GridEditor
37251  * @extends Roo.Editor
37252  * Class for creating and editable grid elements.
37253  * @param {Object} config any settings (must include field)
37254  */
37255 Roo.grid.GridEditor = function(field, config){
37256     if (!config && field.field) {
37257         config = field;
37258         field = Roo.factory(config.field, Roo.form);
37259     }
37260     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37261     field.monitorTab = false;
37262 };
37263
37264 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37265     
37266     /**
37267      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37268      */
37269     
37270     alignment: "tl-tl",
37271     autoSize: "width",
37272     hideEl : false,
37273     cls: "x-small-editor x-grid-editor",
37274     shim:false,
37275     shadow:"frame"
37276 });/*
37277  * Based on:
37278  * Ext JS Library 1.1.1
37279  * Copyright(c) 2006-2007, Ext JS, LLC.
37280  *
37281  * Originally Released Under LGPL - original licence link has changed is not relivant.
37282  *
37283  * Fork - LGPL
37284  * <script type="text/javascript">
37285  */
37286   
37287
37288   
37289 Roo.grid.PropertyRecord = Roo.data.Record.create([
37290     {name:'name',type:'string'},  'value'
37291 ]);
37292
37293
37294 Roo.grid.PropertyStore = function(grid, source){
37295     this.grid = grid;
37296     this.store = new Roo.data.Store({
37297         recordType : Roo.grid.PropertyRecord
37298     });
37299     this.store.on('update', this.onUpdate,  this);
37300     if(source){
37301         this.setSource(source);
37302     }
37303     Roo.grid.PropertyStore.superclass.constructor.call(this);
37304 };
37305
37306
37307
37308 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37309     setSource : function(o){
37310         this.source = o;
37311         this.store.removeAll();
37312         var data = [];
37313         for(var k in o){
37314             if(this.isEditableValue(o[k])){
37315                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37316             }
37317         }
37318         this.store.loadRecords({records: data}, {}, true);
37319     },
37320
37321     onUpdate : function(ds, record, type){
37322         if(type == Roo.data.Record.EDIT){
37323             var v = record.data['value'];
37324             var oldValue = record.modified['value'];
37325             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37326                 this.source[record.id] = v;
37327                 record.commit();
37328                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37329             }else{
37330                 record.reject();
37331             }
37332         }
37333     },
37334
37335     getProperty : function(row){
37336        return this.store.getAt(row);
37337     },
37338
37339     isEditableValue: function(val){
37340         if(val && val instanceof Date){
37341             return true;
37342         }else if(typeof val == 'object' || typeof val == 'function'){
37343             return false;
37344         }
37345         return true;
37346     },
37347
37348     setValue : function(prop, value){
37349         this.source[prop] = value;
37350         this.store.getById(prop).set('value', value);
37351     },
37352
37353     getSource : function(){
37354         return this.source;
37355     }
37356 });
37357
37358 Roo.grid.PropertyColumnModel = function(grid, store){
37359     this.grid = grid;
37360     var g = Roo.grid;
37361     g.PropertyColumnModel.superclass.constructor.call(this, [
37362         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37363         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37364     ]);
37365     this.store = store;
37366     this.bselect = Roo.DomHelper.append(document.body, {
37367         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37368             {tag: 'option', value: 'true', html: 'true'},
37369             {tag: 'option', value: 'false', html: 'false'}
37370         ]
37371     });
37372     Roo.id(this.bselect);
37373     var f = Roo.form;
37374     this.editors = {
37375         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37376         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37377         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37378         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37379         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37380     };
37381     this.renderCellDelegate = this.renderCell.createDelegate(this);
37382     this.renderPropDelegate = this.renderProp.createDelegate(this);
37383 };
37384
37385 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37386     
37387     
37388     nameText : 'Name',
37389     valueText : 'Value',
37390     
37391     dateFormat : 'm/j/Y',
37392     
37393     
37394     renderDate : function(dateVal){
37395         return dateVal.dateFormat(this.dateFormat);
37396     },
37397
37398     renderBool : function(bVal){
37399         return bVal ? 'true' : 'false';
37400     },
37401
37402     isCellEditable : function(colIndex, rowIndex){
37403         return colIndex == 1;
37404     },
37405
37406     getRenderer : function(col){
37407         return col == 1 ?
37408             this.renderCellDelegate : this.renderPropDelegate;
37409     },
37410
37411     renderProp : function(v){
37412         return this.getPropertyName(v);
37413     },
37414
37415     renderCell : function(val){
37416         var rv = val;
37417         if(val instanceof Date){
37418             rv = this.renderDate(val);
37419         }else if(typeof val == 'boolean'){
37420             rv = this.renderBool(val);
37421         }
37422         return Roo.util.Format.htmlEncode(rv);
37423     },
37424
37425     getPropertyName : function(name){
37426         var pn = this.grid.propertyNames;
37427         return pn && pn[name] ? pn[name] : name;
37428     },
37429
37430     getCellEditor : function(colIndex, rowIndex){
37431         var p = this.store.getProperty(rowIndex);
37432         var n = p.data['name'], val = p.data['value'];
37433         
37434         if(typeof(this.grid.customEditors[n]) == 'string'){
37435             return this.editors[this.grid.customEditors[n]];
37436         }
37437         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37438             return this.grid.customEditors[n];
37439         }
37440         if(val instanceof Date){
37441             return this.editors['date'];
37442         }else if(typeof val == 'number'){
37443             return this.editors['number'];
37444         }else if(typeof val == 'boolean'){
37445             return this.editors['boolean'];
37446         }else{
37447             return this.editors['string'];
37448         }
37449     }
37450 });
37451
37452 /**
37453  * @class Roo.grid.PropertyGrid
37454  * @extends Roo.grid.EditorGrid
37455  * This class represents the  interface of a component based property grid control.
37456  * <br><br>Usage:<pre><code>
37457  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37458       
37459  });
37460  // set any options
37461  grid.render();
37462  * </code></pre>
37463   
37464  * @constructor
37465  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37466  * The container MUST have some type of size defined for the grid to fill. The container will be
37467  * automatically set to position relative if it isn't already.
37468  * @param {Object} config A config object that sets properties on this grid.
37469  */
37470 Roo.grid.PropertyGrid = function(container, config){
37471     config = config || {};
37472     var store = new Roo.grid.PropertyStore(this);
37473     this.store = store;
37474     var cm = new Roo.grid.PropertyColumnModel(this, store);
37475     store.store.sort('name', 'ASC');
37476     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37477         ds: store.store,
37478         cm: cm,
37479         enableColLock:false,
37480         enableColumnMove:false,
37481         stripeRows:false,
37482         trackMouseOver: false,
37483         clicksToEdit:1
37484     }, config));
37485     this.getGridEl().addClass('x-props-grid');
37486     this.lastEditRow = null;
37487     this.on('columnresize', this.onColumnResize, this);
37488     this.addEvents({
37489          /**
37490              * @event beforepropertychange
37491              * Fires before a property changes (return false to stop?)
37492              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37493              * @param {String} id Record Id
37494              * @param {String} newval New Value
37495          * @param {String} oldval Old Value
37496              */
37497         "beforepropertychange": true,
37498         /**
37499              * @event propertychange
37500              * Fires after a property changes
37501              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37502              * @param {String} id Record Id
37503              * @param {String} newval New Value
37504          * @param {String} oldval Old Value
37505              */
37506         "propertychange": true
37507     });
37508     this.customEditors = this.customEditors || {};
37509 };
37510 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37511     
37512      /**
37513      * @cfg {Object} customEditors map of colnames=> custom editors.
37514      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37515      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37516      * false disables editing of the field.
37517          */
37518     
37519       /**
37520      * @cfg {Object} propertyNames map of property Names to their displayed value
37521          */
37522     
37523     render : function(){
37524         Roo.grid.PropertyGrid.superclass.render.call(this);
37525         this.autoSize.defer(100, this);
37526     },
37527
37528     autoSize : function(){
37529         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37530         if(this.view){
37531             this.view.fitColumns();
37532         }
37533     },
37534
37535     onColumnResize : function(){
37536         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37537         this.autoSize();
37538     },
37539     /**
37540      * Sets the data for the Grid
37541      * accepts a Key => Value object of all the elements avaiable.
37542      * @param {Object} data  to appear in grid.
37543      */
37544     setSource : function(source){
37545         this.store.setSource(source);
37546         //this.autoSize();
37547     },
37548     /**
37549      * Gets all the data from the grid.
37550      * @return {Object} data  data stored in grid
37551      */
37552     getSource : function(){
37553         return this.store.getSource();
37554     }
37555 });/*
37556  * Based on:
37557  * Ext JS Library 1.1.1
37558  * Copyright(c) 2006-2007, Ext JS, LLC.
37559  *
37560  * Originally Released Under LGPL - original licence link has changed is not relivant.
37561  *
37562  * Fork - LGPL
37563  * <script type="text/javascript">
37564  */
37565  
37566 /**
37567  * @class Roo.LoadMask
37568  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37569  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37570  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37571  * element's UpdateManager load indicator and will be destroyed after the initial load.
37572  * @constructor
37573  * Create a new LoadMask
37574  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37575  * @param {Object} config The config object
37576  */
37577 Roo.LoadMask = function(el, config){
37578     this.el = Roo.get(el);
37579     Roo.apply(this, config);
37580     if(this.store){
37581         this.store.on('beforeload', this.onBeforeLoad, this);
37582         this.store.on('load', this.onLoad, this);
37583         this.store.on('loadexception', this.onLoadException, this);
37584         this.removeMask = false;
37585     }else{
37586         var um = this.el.getUpdateManager();
37587         um.showLoadIndicator = false; // disable the default indicator
37588         um.on('beforeupdate', this.onBeforeLoad, this);
37589         um.on('update', this.onLoad, this);
37590         um.on('failure', this.onLoad, this);
37591         this.removeMask = true;
37592     }
37593 };
37594
37595 Roo.LoadMask.prototype = {
37596     /**
37597      * @cfg {Boolean} removeMask
37598      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37599      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37600      */
37601     /**
37602      * @cfg {String} msg
37603      * The text to display in a centered loading message box (defaults to 'Loading...')
37604      */
37605     msg : 'Loading...',
37606     /**
37607      * @cfg {String} msgCls
37608      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37609      */
37610     msgCls : 'x-mask-loading',
37611
37612     /**
37613      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37614      * @type Boolean
37615      */
37616     disabled: false,
37617
37618     /**
37619      * Disables the mask to prevent it from being displayed
37620      */
37621     disable : function(){
37622        this.disabled = true;
37623     },
37624
37625     /**
37626      * Enables the mask so that it can be displayed
37627      */
37628     enable : function(){
37629         this.disabled = false;
37630     },
37631     
37632     onLoadException : function()
37633     {
37634         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37635             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37636         }
37637         this.el.unmask(this.removeMask);
37638     },
37639     // private
37640     onLoad : function()
37641     {
37642         this.el.unmask(this.removeMask);
37643     },
37644
37645     // private
37646     onBeforeLoad : function(){
37647         if(!this.disabled){
37648             this.el.mask(this.msg, this.msgCls);
37649         }
37650     },
37651
37652     // private
37653     destroy : function(){
37654         if(this.store){
37655             this.store.un('beforeload', this.onBeforeLoad, this);
37656             this.store.un('load', this.onLoad, this);
37657             this.store.un('loadexception', this.onLoadException, this);
37658         }else{
37659             var um = this.el.getUpdateManager();
37660             um.un('beforeupdate', this.onBeforeLoad, this);
37661             um.un('update', this.onLoad, this);
37662             um.un('failure', this.onLoad, this);
37663         }
37664     }
37665 };/*
37666  * Based on:
37667  * Ext JS Library 1.1.1
37668  * Copyright(c) 2006-2007, Ext JS, LLC.
37669  *
37670  * Originally Released Under LGPL - original licence link has changed is not relivant.
37671  *
37672  * Fork - LGPL
37673  * <script type="text/javascript">
37674  */
37675 Roo.XTemplate = function(){
37676     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37677     var s = this.html;
37678
37679     s = ['<tpl>', s, '</tpl>'].join('');
37680
37681     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
37682
37683     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
37684     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
37685     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
37686     var m, id = 0;
37687     var tpls = [];
37688
37689     while(m = s.match(re)){
37690        var m2 = m[0].match(nameRe);
37691        var m3 = m[0].match(ifRe);
37692        var m4 = m[0].match(execRe);
37693        var exp = null, fn = null, exec = null;
37694        var name = m2 && m2[1] ? m2[1] : '';
37695        if(m3){
37696            exp = m3 && m3[1] ? m3[1] : null;
37697            if(exp){
37698                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
37699            }
37700        }
37701        if(m4){
37702            exp = m4 && m4[1] ? m4[1] : null;
37703            if(exp){
37704                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
37705            }
37706        }
37707        if(name){
37708            switch(name){
37709                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
37710                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
37711                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
37712            }
37713        }
37714        tpls.push({
37715             id: id,
37716             target: name,
37717             exec: exec,
37718             test: fn,
37719             body: m[1]||''
37720         });
37721        s = s.replace(m[0], '{xtpl'+ id + '}');
37722        ++id;
37723     }
37724     for(var i = tpls.length-1; i >= 0; --i){
37725         this.compileTpl(tpls[i]);
37726     }
37727     this.master = tpls[tpls.length-1];
37728     this.tpls = tpls;
37729 };
37730 Roo.extend(Roo.XTemplate, Roo.Template, {
37731
37732     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37733
37734     applySubTemplate : function(id, values, parent){
37735         var t = this.tpls[id];
37736         if(t.test && !t.test.call(this, values, parent)){
37737             return '';
37738         }
37739         if(t.exec && t.exec.call(this, values, parent)){
37740             return '';
37741         }
37742         var vs = t.target ? t.target.call(this, values, parent) : values;
37743         parent = t.target ? values : parent;
37744         if(t.target && vs instanceof Array){
37745             var buf = [];
37746             for(var i = 0, len = vs.length; i < len; i++){
37747                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
37748             }
37749             return buf.join('');
37750         }
37751         return t.compiled.call(this, vs, parent);
37752     },
37753
37754     compileTpl : function(tpl){
37755         var fm = Roo.util.Format;
37756         var useF = this.disableFormats !== true;
37757         var sep = Roo.isGecko ? "+" : ",";
37758         var fn = function(m, name, format, args){
37759             if(name.substr(0, 4) == 'xtpl'){
37760                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
37761             }
37762             var v;
37763             if(name.indexOf('.') != -1){
37764                 v = name;
37765             }else{
37766                 v = "values['" + name + "']";
37767             }
37768             if(format && useF){
37769                 args = args ? ',' + args : "";
37770                 if(format.substr(0, 5) != "this."){
37771                     format = "fm." + format + '(';
37772                 }else{
37773                     format = 'this.call("'+ format.substr(5) + '", ';
37774                     args = ", values";
37775                 }
37776             }else{
37777                 args= ''; format = "("+v+" === undefined ? '' : ";
37778             }
37779             return "'"+ sep + format + v + args + ")"+sep+"'";
37780         };
37781         var body;
37782         // branched to use + in gecko and [].join() in others
37783         if(Roo.isGecko){
37784             body = "tpl.compiled = function(values, parent){ return '" +
37785                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
37786                     "';};";
37787         }else{
37788             body = ["tpl.compiled = function(values, parent){ return ['"];
37789             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
37790             body.push("'].join('');};");
37791             body = body.join('');
37792         }
37793         /** eval:var:zzzzzzz */
37794         eval(body);
37795         return this;
37796     },
37797
37798     applyTemplate : function(values){
37799         return this.master.compiled.call(this, values, {});
37800         var s = this.subs;
37801     },
37802
37803     apply : function(){
37804         return this.applyTemplate.apply(this, arguments);
37805     },
37806
37807     compile : function(){return this;}
37808 });
37809
37810 Roo.XTemplate.from = function(el){
37811     el = Roo.getDom(el);
37812     return new Roo.XTemplate(el.value || el.innerHTML);
37813 };/*
37814  * Original code for Roojs - LGPL
37815  * <script type="text/javascript">
37816  */
37817  
37818 /**
37819  * @class Roo.XComponent
37820  * A delayed Element creator...
37821  * Or a way to group chunks of interface together.
37822  * 
37823  * Mypart.xyx = new Roo.XComponent({
37824
37825     parent : 'Mypart.xyz', // empty == document.element.!!
37826     order : '001',
37827     name : 'xxxx'
37828     region : 'xxxx'
37829     disabled : function() {} 
37830      
37831     tree : function() { // return an tree of xtype declared components
37832         var MODULE = this;
37833         return 
37834         {
37835             xtype : 'NestedLayoutPanel',
37836             // technicall
37837         }
37838      ]
37839  *})
37840  *
37841  *
37842  * It can be used to build a big heiracy, with parent etc.
37843  * or you can just use this to render a single compoent to a dom element
37844  * MYPART.render(Roo.Element | String(id) | dom_element )
37845  * 
37846  * @extends Roo.util.Observable
37847  * @constructor
37848  * @param cfg {Object} configuration of component
37849  * 
37850  */
37851 Roo.XComponent = function(cfg) {
37852     Roo.apply(this, cfg);
37853     this.addEvents({ 
37854         /**
37855              * @event built
37856              * Fires when this the componnt is built
37857              * @param {Roo.XComponent} c the component
37858              */
37859         'built' : true,
37860         /**
37861              * @event buildcomplete
37862              * Fires on the top level element when all elements have been built
37863              * @param {Roo.XComponent} c the top level component.
37864          */
37865         'buildcomplete' : true
37866         
37867     });
37868     this.region = this.region || 'center'; // default..
37869     Roo.XComponent.register(this);
37870     this.modules = false;
37871     this.el = false; // where the layout goes..
37872     
37873     
37874 }
37875 Roo.extend(Roo.XComponent, Roo.util.Observable, {
37876     /**
37877      * @property el
37878      * The created element (with Roo.factory())
37879      * @type {Roo.Layout}
37880      */
37881     el  : false,
37882     
37883     /**
37884      * @property el
37885      * for BC  - use el in new code
37886      * @type {Roo.Layout}
37887      */
37888     panel : false,
37889     
37890     /**
37891      * @property layout
37892      * for BC  - use el in new code
37893      * @type {Roo.Layout}
37894      */
37895     layout : false,
37896     
37897      /**
37898      * @cfg {Function|boolean} disabled
37899      * If this module is disabled by some rule, return true from the funtion
37900      */
37901     disabled : false,
37902     
37903     /**
37904      * @cfg {String} parent 
37905      * Name of parent element which it get xtype added to..
37906      */
37907     parent: false,
37908     
37909     /**
37910      * @cfg {String} order
37911      * Used to set the order in which elements are created (usefull for multiple tabs)
37912      */
37913     
37914     order : false,
37915     /**
37916      * @cfg {String} name
37917      * String to display while loading.
37918      */
37919     name : false,
37920     /**
37921      * @cfg {String} region
37922      * Region to render component to (defaults to center)
37923      */
37924     region : 'center',
37925     
37926     /**
37927      * @cfg {Array} items
37928      * A single item array - the first element is the root of the tree..
37929      * It's done this way to stay compatible with the Xtype system...
37930      */
37931     items : false,
37932     
37933     
37934      /**
37935      * render
37936      * render element to dom or tree
37937      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
37938      */
37939     
37940     render : function(el)
37941     {
37942         
37943         el = el || false;
37944         var hp = this.parent ? 1 : 0;
37945         
37946         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
37947             // if parent is a '#.....' string, then let's use that..
37948             var ename = this.parent.substr(1)
37949             this.parent = false;
37950             el = Roo.get(ename);
37951             if (!el) {
37952                 Roo.log("Warning - element can not be found :#" + ename );
37953                 return;
37954             }
37955         }
37956         
37957         
37958         if (!this.parent) {
37959             
37960             el = el ? Roo.get(el) : false;
37961             
37962             // it's a top level one..
37963             this.parent =  {
37964                 el : new Roo.BorderLayout(el || document.body, {
37965                 
37966                      center: {
37967                          titlebar: false,
37968                          autoScroll:false,
37969                          closeOnTab: true,
37970                          tabPosition: 'top',
37971                           //resizeTabs: true,
37972                          alwaysShowTabs: el && hp? false :  true,
37973                          hideTabs: el || !hp ? true :  false,
37974                          minTabWidth: 140
37975                      }
37976                  })
37977             }
37978         }
37979         
37980         
37981             
37982         var tree = this.tree();
37983         tree.region = tree.region || this.region;
37984         this.el = this.parent.el.addxtype(tree);
37985         this.fireEvent('built', this);
37986         
37987         this.panel = this.el;
37988         this.layout = this.panel.layout;    
37989          
37990     }
37991     
37992 });
37993
37994 Roo.apply(Roo.XComponent, {
37995     
37996     /**
37997      * @property  buildCompleted
37998      * True when the builder has completed building the interface.
37999      * @type Boolean
38000      */
38001     buildCompleted : false,
38002      
38003     /**
38004      * @property  topModule
38005      * the upper most module - uses document.element as it's constructor.
38006      * @type Object
38007      */
38008      
38009     topModule  : false,
38010       
38011     /**
38012      * @property  modules
38013      * array of modules to be created by registration system.
38014      * @type {Array} of Roo.XComponent
38015      */
38016     
38017     modules : [],
38018     /**
38019      * @property  elmodules
38020      * array of modules to be created by which use #ID 
38021      * @type {Array} of Roo.XComponent
38022      */
38023      
38024     elmodules : [],
38025
38026     
38027     /**
38028      * Register components to be built later.
38029      *
38030      * This solves the following issues
38031      * - Building is not done on page load, but after an authentication process has occured.
38032      * - Interface elements are registered on page load
38033      * - Parent Interface elements may not be loaded before child, so this handles that..
38034      * 
38035      *
38036      * example:
38037      * 
38038      * MyApp.register({
38039           order : '000001',
38040           module : 'Pman.Tab.projectMgr',
38041           region : 'center',
38042           parent : 'Pman.layout',
38043           disabled : false,  // or use a function..
38044         })
38045      
38046      * * @param {Object} details about module
38047      */
38048     register : function(obj) {
38049         this.modules.push(obj);
38050          
38051     },
38052     /**
38053      * convert a string to an object..
38054      * eg. 'AAA.BBB' -> finds AAA.BBB
38055
38056      */
38057     
38058     toObject : function(str)
38059     {
38060         if (!str || typeof(str) == 'object') {
38061             return str;
38062         }
38063         if (str.substring(0,1) == '#') {
38064             return str;
38065         }
38066
38067         var ar = str.split('.');
38068         var rt, o;
38069         rt = ar.shift();
38070             /** eval:var:o */
38071         try {
38072             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38073         } catch (e) {
38074             throw "Module not found : " + str;
38075         }
38076         
38077         if (o === false) {
38078             throw "Module not found : " + str;
38079         }
38080         Roo.each(ar, function(e) {
38081             if (typeof(o[e]) == 'undefined') {
38082                 throw "Module not found : " + str;
38083             }
38084             o = o[e];
38085         });
38086         
38087         return o;
38088         
38089     },
38090     
38091     
38092     /**
38093      * move modules into their correct place in the tree..
38094      * 
38095      */
38096     preBuild : function ()
38097     {
38098         var _t = this;
38099         Roo.each(this.modules , function (obj)
38100         {
38101             var opar = obj.parent;
38102             try { 
38103                 obj.parent = this.toObject(opar);
38104             } catch(e) {
38105                 Roo.log(e.toString());
38106                 return;
38107             }
38108             
38109             if (!obj.parent) {
38110                 this.topModule = obj;
38111                 return;
38112             }
38113             if (typeof(obj.parent) == 'string') {
38114                 this.elmodules.push(obj);
38115                 return;
38116             }
38117             if (obj.parent.constructor != Roo.XComponent) {
38118                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
38119             }
38120             if (!obj.parent.modules) {
38121                 obj.parent.modules = new Roo.util.MixedCollection(false, 
38122                     function(o) { return o.order + '' }
38123                 );
38124             }
38125             
38126             obj.parent.modules.add(obj);
38127         }, this);
38128     },
38129     
38130      /**
38131      * make a list of modules to build.
38132      * @return {Array} list of modules. 
38133      */ 
38134     
38135     buildOrder : function()
38136     {
38137         var _this = this;
38138         var cmp = function(a,b) {   
38139             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
38140         };
38141         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
38142             throw "No top level modules to build";
38143         }
38144         
38145         // make a flat list in order of modules to build.
38146         var mods = this.topModule ? [ this.topModule ] : [];
38147         Roo.each(this.elmodules,function(e) { mods.push(e) });
38148
38149         
38150         // add modules to their parents..
38151         var addMod = function(m) {
38152            // Roo.debug && Roo.log(m.modKey);
38153             
38154             mods.push(m);
38155             if (m.modules) {
38156                 m.modules.keySort('ASC',  cmp );
38157                 m.modules.each(addMod);
38158             }
38159             // not sure if this is used any more..
38160             if (m.finalize) {
38161                 m.finalize.name = m.name + " (clean up) ";
38162                 mods.push(m.finalize);
38163             }
38164             
38165         }
38166         if (this.topModule) { 
38167             this.topModule.modules.keySort('ASC',  cmp );
38168             this.topModule.modules.each(addMod);
38169         }
38170         return mods;
38171     },
38172     
38173      /**
38174      * Build the registered modules.
38175      * @param {Object} parent element.
38176      * @param {Function} optional method to call after module has been added.
38177      * 
38178      */ 
38179    
38180     build : function() 
38181     {
38182         
38183         this.preBuild();
38184         var mods = this.buildOrder();
38185       
38186         //this.allmods = mods;
38187         //Roo.debug && Roo.log(mods);
38188         //return;
38189         if (!mods.length) { // should not happen
38190             throw "NO modules!!!";
38191         }
38192         
38193         
38194         
38195         // flash it up as modal - so we store the mask!?
38196         Roo.MessageBox.show({ title: 'loading' });
38197         Roo.MessageBox.show({
38198            title: "Please wait...",
38199            msg: "Building Interface...",
38200            width:450,
38201            progress:true,
38202            closable:false,
38203            modal: false
38204           
38205         });
38206         var total = mods.length;
38207         
38208         var _this = this;
38209         var progressRun = function() {
38210             if (!mods.length) {
38211                 Roo.debug && Roo.log('hide?');
38212                 Roo.MessageBox.hide();
38213                 if (_this.topModule) { 
38214                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
38215                 }
38216                 // THE END...
38217                 return false;   
38218             }
38219             
38220             var m = mods.shift();
38221             
38222             
38223             Roo.debug && Roo.log(m);
38224             // not sure if this is supported any more.. - modules that are are just function
38225             if (typeof(m) == 'function') { 
38226                 m.call(this);
38227                 return progressRun.defer(10, _this);
38228             } 
38229             
38230             
38231             
38232             Roo.MessageBox.updateProgress(
38233                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
38234                     " of " + total + 
38235                     (m.name ? (' - ' + m.name) : '')
38236                     );
38237             
38238          
38239             // is the module disabled?
38240             var disabled = (typeof(m.disabled) == 'function') ?
38241                 m.disabled.call(m.module.disabled) : m.disabled;    
38242             
38243             
38244             if (disabled) {
38245                 return progressRun(); // we do not update the display!
38246             }
38247             
38248             // now build 
38249             
38250             m.render();
38251             // it's 10 on top level, and 1 on others??? why...
38252             return progressRun.defer(10, _this);
38253              
38254         }
38255         progressRun.defer(1, _this);
38256      
38257         
38258         
38259     }
38260     
38261      
38262    
38263     
38264     
38265 });
38266  //<script type="text/javascript">
38267
38268
38269 /**
38270  * @class Roo.Login
38271  * @extends Roo.LayoutDialog
38272  * A generic Login Dialog..... - only one needed in theory!?!?
38273  *
38274  * Fires XComponent builder on success...
38275  * 
38276  * Sends 
38277  *    username,password, lang = for login actions.
38278  *    check = 1 for periodic checking that sesion is valid.
38279  *    passwordRequest = email request password
38280  *    logout = 1 = to logout
38281  * 
38282  * Affects: (this id="????" elements)
38283  *   loading  (removed) (used to indicate application is loading)
38284  *   loading-mask (hides) (used to hide application when it's building loading)
38285  *   
38286  * 
38287  * Usage: 
38288  *    
38289  * 
38290  * Myapp.login = Roo.Login({
38291      url: xxxx,
38292    
38293      realm : 'Myapp', 
38294      
38295      
38296      method : 'POST',
38297      
38298      
38299      * 
38300  })
38301  * 
38302  * 
38303  * 
38304  **/
38305  
38306 Roo.Login = function(cfg)
38307 {
38308     this.addEvents({
38309         'refreshed' : true
38310     });
38311     
38312     Roo.apply(this,cfg);
38313     
38314     Roo.onReady(function() {
38315         this.onLoad();
38316     }, this);
38317     // call parent..
38318     
38319    
38320     Roo.Login.superclass.constructor.call(this, this);
38321     //this.addxtype(this.items[0]);
38322     
38323     
38324 }
38325
38326
38327 Roo.extend(Roo.Login, Roo.LayoutDialog, {
38328     
38329     /**
38330      * @cfg {String} method
38331      * Method used to query for login details.
38332      */
38333     
38334     method : 'POST',
38335     /**
38336      * @cfg {String} url
38337      * URL to query login data. - eg. baseURL + '/Login.php'
38338      */
38339     url : '',
38340     
38341     /**
38342      * @property user
38343      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
38344      * @type {Object} 
38345      */
38346     user : false,
38347     /**
38348      * @property checkFails
38349      * Number of times we have attempted to get authentication check, and failed.
38350      * @type {Number} 
38351      */
38352     checkFails : 0,
38353       /**
38354      * @property intervalID
38355      * The window interval that does the constant login checking.
38356      * @type {Number} 
38357      */
38358     intervalID : 0,
38359     
38360     
38361     onLoad : function() // called on page load...
38362     {
38363         // load 
38364          
38365         if (Roo.get('loading')) { // clear any loading indicator..
38366             Roo.get('loading').remove();
38367         }
38368         
38369         //this.switchLang('en'); // set the language to english..
38370        
38371         this.check({
38372             success:  function(response, opts)  {  // check successfull...
38373             
38374                 var res = this.processResponse(response);
38375                 this.checkFails =0;
38376                 if (!res.success) { // error!
38377                     this.checkFails = 5;
38378                     //console.log('call failure');
38379                     return this.failure(response,opts);
38380                 }
38381                 
38382                 if (!res.data.id) { // id=0 == login failure.
38383                     return this.show();
38384                 }
38385                 
38386                               
38387                         //console.log(success);
38388                 this.fillAuth(res.data);   
38389                 this.checkFails =0;
38390                 Roo.XComponent.build();
38391             },
38392             failure : this.show
38393         });
38394         
38395     }, 
38396     
38397     
38398     check: function(cfg) // called every so often to refresh cookie etc..
38399     {
38400         if (cfg.again) { // could be undefined..
38401             this.checkFails++;
38402         } else {
38403             this.checkFails = 0;
38404         }
38405         var _this = this;
38406         if (this.sending) {
38407             if ( this.checkFails > 4) {
38408                 Roo.MessageBox.alert("Error",  
38409                     "Error getting authentication status. - try reloading, or wait a while", function() {
38410                         _this.sending = false;
38411                     }); 
38412                 return;
38413             }
38414             cfg.again = true;
38415             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
38416             return;
38417         }
38418         this.sending = true;
38419         
38420         Roo.Ajax.request({  
38421             url: this.url,
38422             params: {
38423                 getAuthUser: true
38424             },  
38425             method: this.method,
38426             success:  cfg.success || this.success,
38427             failure : cfg.failure || this.failure,
38428             scope : this,
38429             callCfg : cfg
38430               
38431         });  
38432     }, 
38433     
38434     
38435     logout: function()
38436     {
38437         window.onbeforeunload = function() { }; // false does not work for IE..
38438         this.user = false;
38439         var _this = this;
38440         
38441         Roo.Ajax.request({  
38442             url: this.url,
38443             params: {
38444                 logout: 1
38445             },  
38446             method: 'GET',
38447             failure : function() {
38448                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
38449                     document.location = document.location.toString() + '?ts=' + Math.random();
38450                 });
38451                 
38452             },
38453             success : function() {
38454                 _this.user = false;
38455                 this.checkFails =0;
38456                 // fixme..
38457                 document.location = document.location.toString() + '?ts=' + Math.random();
38458             }
38459               
38460               
38461         }); 
38462     },
38463     
38464     processResponse : function (response)
38465     {
38466         var res = '';
38467         try {
38468             res = Roo.decode(response.responseText);
38469             // oops...
38470             if (typeof(res) != 'object') {
38471                 res = { success : false, errorMsg : res, errors : true };
38472             }
38473             if (typeof(res.success) == 'undefined') {
38474                 res.success = false;
38475             }
38476             
38477         } catch(e) {
38478             res = { success : false,  errorMsg : response.responseText, errors : true };
38479         }
38480         return res;
38481     },
38482     
38483     success : function(response, opts)  // check successfull...
38484     {  
38485         this.sending = false;
38486         var res = this.processResponse(response);
38487         if (!res.success) {
38488             return this.failure(response, opts);
38489         }
38490         if (!res.data || !res.data.id) {
38491             return this.failure(response,opts);
38492         }
38493         //console.log(res);
38494         this.fillAuth(res.data);
38495         
38496         this.checkFails =0;
38497         
38498     },
38499     
38500     
38501     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
38502     {
38503         this.authUser = -1;
38504         this.sending = false;
38505         var res = this.processResponse(response);
38506         //console.log(res);
38507         if ( this.checkFails > 2) {
38508         
38509             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
38510                 "Error getting authentication status. - try reloading"); 
38511             return;
38512         }
38513         opts.callCfg.again = true;
38514         this.check.defer(1000, this, [ opts.callCfg ]);
38515         return;  
38516     },
38517     
38518     
38519     
38520     fillAuth: function(au) {
38521         this.startAuthCheck();
38522         this.authUserId = au.id;
38523         this.authUser = au;
38524         this.lastChecked = new Date();
38525         this.fireEvent('refreshed', au);
38526         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
38527         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
38528         au.lang = au.lang || 'en';
38529         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
38530         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
38531         this.switchLang(au.lang );
38532         
38533      
38534         // open system... - -on setyp..
38535         if (this.authUserId  < 0) {
38536             Roo.MessageBox.alert("Warning", 
38537                 "This is an open system - please set up a admin user with a password.");  
38538         }
38539          
38540         //Pman.onload(); // which should do nothing if it's a re-auth result...
38541         
38542              
38543     },
38544     
38545     startAuthCheck : function() // starter for timeout checking..
38546     {
38547         if (this.intervalID) { // timer already in place...
38548             return false;
38549         }
38550         var _this = this;
38551         this.intervalID =  window.setInterval(function() {
38552               _this.check(false);
38553             }, 120000); // every 120 secs = 2mins..
38554         
38555         
38556     },
38557          
38558     
38559     switchLang : function (lang) 
38560     {
38561         _T = typeof(_T) == 'undefined' ? false : _T;
38562           if (!_T || !lang.length) {
38563             return;
38564         }
38565         
38566         if (!_T && lang != 'en') {
38567             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38568             return;
38569         }
38570         
38571         if (typeof(_T.en) == 'undefined') {
38572             _T.en = {};
38573             Roo.apply(_T.en, _T);
38574         }
38575         
38576         if (typeof(_T[lang]) == 'undefined') {
38577             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
38578             return;
38579         }
38580         
38581         
38582         Roo.apply(_T, _T[lang]);
38583         // just need to set the text values for everything...
38584         var _this = this;
38585         /* this will not work ...
38586         if (this.form) { 
38587             
38588                
38589             function formLabel(name, val) {
38590                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
38591             }
38592             
38593             formLabel('password', "Password"+':');
38594             formLabel('username', "Email Address"+':');
38595             formLabel('lang', "Language"+':');
38596             this.dialog.setTitle("Login");
38597             this.dialog.buttons[0].setText("Forgot Password");
38598             this.dialog.buttons[1].setText("Login");
38599         }
38600         */
38601         
38602         
38603     },
38604     
38605     
38606     title: "Login",
38607     modal: true,
38608     width:  350,
38609     //height: 230,
38610     height: 180,
38611     shadow: true,
38612     minWidth:200,
38613     minHeight:180,
38614     //proxyDrag: true,
38615     closable: false,
38616     draggable: false,
38617     collapsible: false,
38618     resizable: false,
38619     center: {  // needed??
38620         autoScroll:false,
38621         titlebar: false,
38622        // tabPosition: 'top',
38623         hideTabs: true,
38624         closeOnTab: true,
38625         alwaysShowTabs: false
38626     } ,
38627     listeners : {
38628         
38629         show  : function(dlg)
38630         {
38631             //console.log(this);
38632             this.form = this.layout.getRegion('center').activePanel.form;
38633             this.form.dialog = dlg;
38634             this.buttons[0].form = this.form;
38635             this.buttons[0].dialog = dlg;
38636             this.buttons[1].form = this.form;
38637             this.buttons[1].dialog = dlg;
38638            
38639            //this.resizeToLogo.defer(1000,this);
38640             // this is all related to resizing for logos..
38641             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
38642            //// if (!sz) {
38643              //   this.resizeToLogo.defer(1000,this);
38644              //   return;
38645            // }
38646             //var w = Ext.lib.Dom.getViewWidth() - 100;
38647             //var h = Ext.lib.Dom.getViewHeight() - 100;
38648             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
38649             //this.center();
38650             if (this.disabled) {
38651                 this.hide();
38652                 return;
38653             }
38654             
38655             if (this.user.id < 0) { // used for inital setup situations.
38656                 return;
38657             }
38658             
38659             if (this.intervalID) {
38660                 // remove the timer
38661                 window.clearInterval(this.intervalID);
38662                 this.intervalID = false;
38663             }
38664             
38665             
38666             if (Roo.get('loading')) {
38667                 Roo.get('loading').remove();
38668             }
38669             if (Roo.get('loading-mask')) {
38670                 Roo.get('loading-mask').hide();
38671             }
38672             
38673             //incomming._node = tnode;
38674             this.form.reset();
38675             //this.dialog.modal = !modal;
38676             //this.dialog.show();
38677             this.el.unmask(); 
38678             
38679             
38680             this.form.setValues({
38681                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
38682                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
38683             });
38684             
38685             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
38686             if (this.form.findField('username').getValue().length > 0 ){
38687                 this.form.findField('password').focus();
38688             } else {
38689                this.form.findField('username').focus();
38690             }
38691     
38692         }
38693     },
38694     items : [
38695          {
38696        
38697             xtype : 'ContentPanel',
38698             xns : Roo,
38699             region: 'center',
38700             fitToFrame : true,
38701             
38702             items : [
38703     
38704                 {
38705                
38706                     xtype : 'Form',
38707                     xns : Roo.form,
38708                     labelWidth: 100,
38709                     style : 'margin: 10px;',
38710                     
38711                     listeners : {
38712                         actionfailed : function(f, act) {
38713                             // form can return { errors: .... }
38714                                 
38715                             //act.result.errors // invalid form element list...
38716                             //act.result.errorMsg// invalid form element list...
38717                             
38718                             this.dialog.el.unmask();
38719                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
38720                                         "Login failed - communication error - try again.");
38721                                       
38722                         },
38723                         actioncomplete: function(re, act) {
38724                              
38725                             Roo.state.Manager.set(
38726                                 this.dialog.realm + '.username',  
38727                                     this.findField('username').getValue()
38728                             );
38729                             Roo.state.Manager.set(
38730                                 this.dialog.realm + '.lang',  
38731                                 this.findField('lang').getValue() 
38732                             );
38733                             
38734                             this.dialog.fillAuth(act.result.data);
38735                               
38736                             this.dialog.hide();
38737                             
38738                             if (Roo.get('loading-mask')) {
38739                                 Roo.get('loading-mask').show();
38740                             }
38741                             Roo.XComponent.build();
38742                             
38743                              
38744                             
38745                         }
38746                     },
38747                     items : [
38748                         {
38749                             xtype : 'TextField',
38750                             xns : Roo.form,
38751                             fieldLabel: "Email Address",
38752                             name: 'username',
38753                             width:200,
38754                             autoCreate : {tag: "input", type: "text", size: "20"}
38755                         },
38756                         {
38757                             xtype : 'TextField',
38758                             xns : Roo.form,
38759                             fieldLabel: "Password",
38760                             inputType: 'password',
38761                             name: 'password',
38762                             width:200,
38763                             autoCreate : {tag: "input", type: "text", size: "20"},
38764                             listeners : {
38765                                 specialkey : function(e,ev) {
38766                                     if (ev.keyCode == 13) {
38767                                         this.form.dialog.el.mask("Logging in");
38768                                         this.form.doAction('submit', {
38769                                             url: this.form.dialog.url,
38770                                             method: this.form.dialog.method
38771                                         });
38772                                     }
38773                                 }
38774                             }  
38775                         },
38776                         {
38777                             xtype : 'ComboBox',
38778                             xns : Roo.form,
38779                             fieldLabel: "Language",
38780                             name : 'langdisp',
38781                             store: {
38782                                 xtype : 'SimpleStore',
38783                                 fields: ['lang', 'ldisp'],
38784                                 data : [
38785                                     [ 'en', 'English' ],
38786                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
38787                                     [ 'zh_CN', '\u7C21\u4E2D' ]
38788                                 ]
38789                             },
38790                             
38791                             valueField : 'lang',
38792                             hiddenName:  'lang',
38793                             width: 200,
38794                             displayField:'ldisp',
38795                             typeAhead: false,
38796                             editable: false,
38797                             mode: 'local',
38798                             triggerAction: 'all',
38799                             emptyText:'Select a Language...',
38800                             selectOnFocus:true,
38801                             listeners : {
38802                                 select :  function(cb, rec, ix) {
38803                                     this.form.switchLang(rec.data.lang);
38804                                 }
38805                             }
38806                         
38807                         }
38808                     ]
38809                 }
38810                   
38811                 
38812             ]
38813         }
38814     ],
38815     buttons : [
38816         {
38817             xtype : 'Button',
38818             xns : 'Roo',
38819             text : "Forgot Password",
38820             listeners : {
38821                 click : function() {
38822                     //console.log(this);
38823                     var n = this.form.findField('username').getValue();
38824                     if (!n.length) {
38825                         Roo.MessageBox.alert("Error", "Fill in your email address");
38826                         return;
38827                     }
38828                     Roo.Ajax.request({
38829                         url: this.dialog.url,
38830                         params: {
38831                             passwordRequest: n
38832                         },
38833                         method: this.dialog.method,
38834                         success:  function(response, opts)  {  // check successfull...
38835                         
38836                             var res = this.dialog.processResponse(response);
38837                             if (!res.success) { // error!
38838                                Roo.MessageBox.alert("Error" ,
38839                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
38840                                return;
38841                             }
38842                             Roo.MessageBox.alert("Notice" ,
38843                                 "Please check you email for the Password Reset message");
38844                         },
38845                         failure : function() {
38846                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
38847                         }
38848                         
38849                     });
38850                 }
38851             }
38852         },
38853         {
38854             xtype : 'Button',
38855             xns : 'Roo',
38856             text : "Login",
38857             listeners : {
38858                 
38859                 click : function () {
38860                         
38861                     this.dialog.el.mask("Logging in");
38862                     this.form.doAction('submit', {
38863                             url: this.dialog.url,
38864                             method: this.dialog.method
38865                     });
38866                 }
38867             }
38868         }
38869     ]
38870   
38871   
38872 })
38873  
38874
38875
38876