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         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841         Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847             Roo.log('check validator');
848             if (this.clickValidator(e)) {
849                 Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <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,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * 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
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     }
5423 });/*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433
5434 /**
5435  * @class Roo.data.SimpleStore
5436  * @extends Roo.data.Store
5437  * Small helper class to make creating Stores from Array data easier.
5438  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5439  * @cfg {Array} fields An array of field definition objects, or field name strings.
5440  * @cfg {Array} data The multi-dimensional array of data
5441  * @constructor
5442  * @param {Object} config
5443  */
5444 Roo.data.SimpleStore = function(config){
5445     Roo.data.SimpleStore.superclass.constructor.call(this, {
5446         isLocal : true,
5447         reader: new Roo.data.ArrayReader({
5448                 id: config.id
5449             },
5450             Roo.data.Record.create(config.fields)
5451         ),
5452         proxy : new Roo.data.MemoryProxy(config.data)
5453     });
5454     this.load();
5455 };
5456 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5457  * Based on:
5458  * Ext JS Library 1.1.1
5459  * Copyright(c) 2006-2007, Ext JS, LLC.
5460  *
5461  * Originally Released Under LGPL - original licence link has changed is not relivant.
5462  *
5463  * Fork - LGPL
5464  * <script type="text/javascript">
5465  */
5466
5467 /**
5468 /**
5469  * @extends Roo.data.Store
5470  * @class Roo.data.JsonStore
5471  * Small helper class to make creating Stores for JSON data easier. <br/>
5472 <pre><code>
5473 var store = new Roo.data.JsonStore({
5474     url: 'get-images.php',
5475     root: 'images',
5476     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5477 });
5478 </code></pre>
5479  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5480  * JsonReader and HttpProxy (unless inline data is provided).</b>
5481  * @cfg {Array} fields An array of field definition objects, or field name strings.
5482  * @constructor
5483  * @param {Object} config
5484  */
5485 Roo.data.JsonStore = function(c){
5486     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5487         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5488         reader: new Roo.data.JsonReader(c, c.fields)
5489     }));
5490 };
5491 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5492  * Based on:
5493  * Ext JS Library 1.1.1
5494  * Copyright(c) 2006-2007, Ext JS, LLC.
5495  *
5496  * Originally Released Under LGPL - original licence link has changed is not relivant.
5497  *
5498  * Fork - LGPL
5499  * <script type="text/javascript">
5500  */
5501
5502  
5503 Roo.data.Field = function(config){
5504     if(typeof config == "string"){
5505         config = {name: config};
5506     }
5507     Roo.apply(this, config);
5508     
5509     if(!this.type){
5510         this.type = "auto";
5511     }
5512     
5513     var st = Roo.data.SortTypes;
5514     // named sortTypes are supported, here we look them up
5515     if(typeof this.sortType == "string"){
5516         this.sortType = st[this.sortType];
5517     }
5518     
5519     // set default sortType for strings and dates
5520     if(!this.sortType){
5521         switch(this.type){
5522             case "string":
5523                 this.sortType = st.asUCString;
5524                 break;
5525             case "date":
5526                 this.sortType = st.asDate;
5527                 break;
5528             default:
5529                 this.sortType = st.none;
5530         }
5531     }
5532
5533     // define once
5534     var stripRe = /[\$,%]/g;
5535
5536     // prebuilt conversion function for this field, instead of
5537     // switching every time we're reading a value
5538     if(!this.convert){
5539         var cv, dateFormat = this.dateFormat;
5540         switch(this.type){
5541             case "":
5542             case "auto":
5543             case undefined:
5544                 cv = function(v){ return v; };
5545                 break;
5546             case "string":
5547                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5548                 break;
5549             case "int":
5550                 cv = function(v){
5551                     return v !== undefined && v !== null && v !== '' ?
5552                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5553                     };
5554                 break;
5555             case "float":
5556                 cv = function(v){
5557                     return v !== undefined && v !== null && v !== '' ?
5558                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5559                     };
5560                 break;
5561             case "bool":
5562             case "boolean":
5563                 cv = function(v){ return v === true || v === "true" || v == 1; };
5564                 break;
5565             case "date":
5566                 cv = function(v){
5567                     if(!v){
5568                         return '';
5569                     }
5570                     if(v instanceof Date){
5571                         return v;
5572                     }
5573                     if(dateFormat){
5574                         if(dateFormat == "timestamp"){
5575                             return new Date(v*1000);
5576                         }
5577                         return Date.parseDate(v, dateFormat);
5578                     }
5579                     var parsed = Date.parse(v);
5580                     return parsed ? new Date(parsed) : null;
5581                 };
5582              break;
5583             
5584         }
5585         this.convert = cv;
5586     }
5587 };
5588
5589 Roo.data.Field.prototype = {
5590     dateFormat: null,
5591     defaultValue: "",
5592     mapping: null,
5593     sortType : null,
5594     sortDir : "ASC"
5595 };/*
5596  * Based on:
5597  * Ext JS Library 1.1.1
5598  * Copyright(c) 2006-2007, Ext JS, LLC.
5599  *
5600  * Originally Released Under LGPL - original licence link has changed is not relivant.
5601  *
5602  * Fork - LGPL
5603  * <script type="text/javascript">
5604  */
5605  
5606 // Base class for reading structured data from a data source.  This class is intended to be
5607 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5608
5609 /**
5610  * @class Roo.data.DataReader
5611  * Base class for reading structured data from a data source.  This class is intended to be
5612  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5613  */
5614
5615 Roo.data.DataReader = function(meta, recordType){
5616     
5617     this.meta = meta;
5618     
5619     this.recordType = recordType instanceof Array ? 
5620         Roo.data.Record.create(recordType) : recordType;
5621 };
5622
5623 Roo.data.DataReader.prototype = {
5624      /**
5625      * Create an empty record
5626      * @param {Object} data (optional) - overlay some values
5627      * @return {Roo.data.Record} record created.
5628      */
5629     newRow :  function(d) {
5630         var da =  {};
5631         this.recordType.prototype.fields.each(function(c) {
5632             switch( c.type) {
5633                 case 'int' : da[c.name] = 0; break;
5634                 case 'date' : da[c.name] = new Date(); break;
5635                 case 'float' : da[c.name] = 0.0; break;
5636                 case 'boolean' : da[c.name] = false; break;
5637                 default : da[c.name] = ""; break;
5638             }
5639             
5640         });
5641         return new this.recordType(Roo.apply(da, d));
5642     }
5643     
5644 };/*
5645  * Based on:
5646  * Ext JS Library 1.1.1
5647  * Copyright(c) 2006-2007, Ext JS, LLC.
5648  *
5649  * Originally Released Under LGPL - original licence link has changed is not relivant.
5650  *
5651  * Fork - LGPL
5652  * <script type="text/javascript">
5653  */
5654
5655 /**
5656  * @class Roo.data.DataProxy
5657  * @extends Roo.data.Observable
5658  * This class is an abstract base class for implementations which provide retrieval of
5659  * unformatted data objects.<br>
5660  * <p>
5661  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5662  * (of the appropriate type which knows how to parse the data object) to provide a block of
5663  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5664  * <p>
5665  * Custom implementations must implement the load method as described in
5666  * {@link Roo.data.HttpProxy#load}.
5667  */
5668 Roo.data.DataProxy = function(){
5669     this.addEvents({
5670         /**
5671          * @event beforeload
5672          * Fires before a network request is made to retrieve a data object.
5673          * @param {Object} This DataProxy object.
5674          * @param {Object} params The params parameter to the load function.
5675          */
5676         beforeload : true,
5677         /**
5678          * @event load
5679          * Fires before the load method's callback is called.
5680          * @param {Object} This DataProxy object.
5681          * @param {Object} o The data object.
5682          * @param {Object} arg The callback argument object passed to the load function.
5683          */
5684         load : true,
5685         /**
5686          * @event loadexception
5687          * Fires if an Exception occurs during data retrieval.
5688          * @param {Object} This DataProxy object.
5689          * @param {Object} o The data object.
5690          * @param {Object} arg The callback argument object passed to the load function.
5691          * @param {Object} e The Exception.
5692          */
5693         loadexception : true
5694     });
5695     Roo.data.DataProxy.superclass.constructor.call(this);
5696 };
5697
5698 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5699
5700     /**
5701      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5702      */
5703 /*
5704  * Based on:
5705  * Ext JS Library 1.1.1
5706  * Copyright(c) 2006-2007, Ext JS, LLC.
5707  *
5708  * Originally Released Under LGPL - original licence link has changed is not relivant.
5709  *
5710  * Fork - LGPL
5711  * <script type="text/javascript">
5712  */
5713 /**
5714  * @class Roo.data.MemoryProxy
5715  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5716  * to the Reader when its load method is called.
5717  * @constructor
5718  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5719  */
5720 Roo.data.MemoryProxy = function(data){
5721     if (data.data) {
5722         data = data.data;
5723     }
5724     Roo.data.MemoryProxy.superclass.constructor.call(this);
5725     this.data = data;
5726 };
5727
5728 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5729     /**
5730      * Load data from the requested source (in this case an in-memory
5731      * data object passed to the constructor), read the data object into
5732      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5733      * process that block using the passed callback.
5734      * @param {Object} params This parameter is not used by the MemoryProxy class.
5735      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5736      * object into a block of Roo.data.Records.
5737      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5738      * The function must be passed <ul>
5739      * <li>The Record block object</li>
5740      * <li>The "arg" argument from the load function</li>
5741      * <li>A boolean success indicator</li>
5742      * </ul>
5743      * @param {Object} scope The scope in which to call the callback
5744      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5745      */
5746     load : function(params, reader, callback, scope, arg){
5747         params = params || {};
5748         var result;
5749         try {
5750             result = reader.readRecords(this.data);
5751         }catch(e){
5752             this.fireEvent("loadexception", this, arg, null, e);
5753             callback.call(scope, null, arg, false);
5754             return;
5755         }
5756         callback.call(scope, result, arg, true);
5757     },
5758     
5759     // private
5760     update : function(params, records){
5761         
5762     }
5763 });/*
5764  * Based on:
5765  * Ext JS Library 1.1.1
5766  * Copyright(c) 2006-2007, Ext JS, LLC.
5767  *
5768  * Originally Released Under LGPL - original licence link has changed is not relivant.
5769  *
5770  * Fork - LGPL
5771  * <script type="text/javascript">
5772  */
5773 /**
5774  * @class Roo.data.HttpProxy
5775  * @extends Roo.data.DataProxy
5776  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5777  * configured to reference a certain URL.<br><br>
5778  * <p>
5779  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5780  * from which the running page was served.<br><br>
5781  * <p>
5782  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5783  * <p>
5784  * Be aware that to enable the browser to parse an XML document, the server must set
5785  * the Content-Type header in the HTTP response to "text/xml".
5786  * @constructor
5787  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5788  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5789  * will be used to make the request.
5790  */
5791 Roo.data.HttpProxy = function(conn){
5792     Roo.data.HttpProxy.superclass.constructor.call(this);
5793     // is conn a conn config or a real conn?
5794     this.conn = conn;
5795     this.useAjax = !conn || !conn.events;
5796   
5797 };
5798
5799 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5800     // thse are take from connection...
5801     
5802     /**
5803      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5804      */
5805     /**
5806      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5807      * extra parameters to each request made by this object. (defaults to undefined)
5808      */
5809     /**
5810      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5811      *  to each request made by this object. (defaults to undefined)
5812      */
5813     /**
5814      * @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)
5815      */
5816     /**
5817      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5818      */
5819      /**
5820      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5821      * @type Boolean
5822      */
5823   
5824
5825     /**
5826      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5827      * @type Boolean
5828      */
5829     /**
5830      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5831      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5832      * a finer-grained basis than the DataProxy events.
5833      */
5834     getConnection : function(){
5835         return this.useAjax ? Roo.Ajax : this.conn;
5836     },
5837
5838     /**
5839      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5840      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5841      * process that block using the passed callback.
5842      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5843      * for the request to the remote server.
5844      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5845      * object into a block of Roo.data.Records.
5846      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5847      * The function must be passed <ul>
5848      * <li>The Record block object</li>
5849      * <li>The "arg" argument from the load function</li>
5850      * <li>A boolean success indicator</li>
5851      * </ul>
5852      * @param {Object} scope The scope in which to call the callback
5853      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5854      */
5855     load : function(params, reader, callback, scope, arg){
5856         if(this.fireEvent("beforeload", this, params) !== false){
5857             var  o = {
5858                 params : params || {},
5859                 request: {
5860                     callback : callback,
5861                     scope : scope,
5862                     arg : arg
5863                 },
5864                 reader: reader,
5865                 callback : this.loadResponse,
5866                 scope: this
5867             };
5868             if(this.useAjax){
5869                 Roo.applyIf(o, this.conn);
5870                 if(this.activeRequest){
5871                     Roo.Ajax.abort(this.activeRequest);
5872                 }
5873                 this.activeRequest = Roo.Ajax.request(o);
5874             }else{
5875                 this.conn.request(o);
5876             }
5877         }else{
5878             callback.call(scope||this, null, arg, false);
5879         }
5880     },
5881
5882     // private
5883     loadResponse : function(o, success, response){
5884         delete this.activeRequest;
5885         if(!success){
5886             this.fireEvent("loadexception", this, o, response);
5887             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5888             return;
5889         }
5890         var result;
5891         try {
5892             result = o.reader.read(response);
5893         }catch(e){
5894             this.fireEvent("loadexception", this, o, response, e);
5895             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5896             return;
5897         }
5898         
5899         this.fireEvent("load", this, o, o.request.arg);
5900         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5901     },
5902
5903     // private
5904     update : function(dataSet){
5905
5906     },
5907
5908     // private
5909     updateResponse : function(dataSet){
5910
5911     }
5912 });/*
5913  * Based on:
5914  * Ext JS Library 1.1.1
5915  * Copyright(c) 2006-2007, Ext JS, LLC.
5916  *
5917  * Originally Released Under LGPL - original licence link has changed is not relivant.
5918  *
5919  * Fork - LGPL
5920  * <script type="text/javascript">
5921  */
5922
5923 /**
5924  * @class Roo.data.ScriptTagProxy
5925  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5926  * other than the originating domain of the running page.<br><br>
5927  * <p>
5928  * <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
5929  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5930  * <p>
5931  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5932  * source code that is used as the source inside a &lt;script> tag.<br><br>
5933  * <p>
5934  * In order for the browser to process the returned data, the server must wrap the data object
5935  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5936  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5937  * depending on whether the callback name was passed:
5938  * <p>
5939  * <pre><code>
5940 boolean scriptTag = false;
5941 String cb = request.getParameter("callback");
5942 if (cb != null) {
5943     scriptTag = true;
5944     response.setContentType("text/javascript");
5945 } else {
5946     response.setContentType("application/x-json");
5947 }
5948 Writer out = response.getWriter();
5949 if (scriptTag) {
5950     out.write(cb + "(");
5951 }
5952 out.print(dataBlock.toJsonString());
5953 if (scriptTag) {
5954     out.write(");");
5955 }
5956 </pre></code>
5957  *
5958  * @constructor
5959  * @param {Object} config A configuration object.
5960  */
5961 Roo.data.ScriptTagProxy = function(config){
5962     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5963     Roo.apply(this, config);
5964     this.head = document.getElementsByTagName("head")[0];
5965 };
5966
5967 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5968
5969 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5970     /**
5971      * @cfg {String} url The URL from which to request the data object.
5972      */
5973     /**
5974      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5975      */
5976     timeout : 30000,
5977     /**
5978      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5979      * the server the name of the callback function set up by the load call to process the returned data object.
5980      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5981      * javascript output which calls this named function passing the data object as its only parameter.
5982      */
5983     callbackParam : "callback",
5984     /**
5985      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5986      * name to the request.
5987      */
5988     nocache : true,
5989
5990     /**
5991      * Load data from the configured URL, read the data object into
5992      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5993      * process that block using the passed callback.
5994      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5995      * for the request to the remote server.
5996      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5997      * object into a block of Roo.data.Records.
5998      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5999      * The function must be passed <ul>
6000      * <li>The Record block object</li>
6001      * <li>The "arg" argument from the load function</li>
6002      * <li>A boolean success indicator</li>
6003      * </ul>
6004      * @param {Object} scope The scope in which to call the callback
6005      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6006      */
6007     load : function(params, reader, callback, scope, arg){
6008         if(this.fireEvent("beforeload", this, params) !== false){
6009
6010             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6011
6012             var url = this.url;
6013             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6014             if(this.nocache){
6015                 url += "&_dc=" + (new Date().getTime());
6016             }
6017             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6018             var trans = {
6019                 id : transId,
6020                 cb : "stcCallback"+transId,
6021                 scriptId : "stcScript"+transId,
6022                 params : params,
6023                 arg : arg,
6024                 url : url,
6025                 callback : callback,
6026                 scope : scope,
6027                 reader : reader
6028             };
6029             var conn = this;
6030
6031             window[trans.cb] = function(o){
6032                 conn.handleResponse(o, trans);
6033             };
6034
6035             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6036
6037             if(this.autoAbort !== false){
6038                 this.abort();
6039             }
6040
6041             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6042
6043             var script = document.createElement("script");
6044             script.setAttribute("src", url);
6045             script.setAttribute("type", "text/javascript");
6046             script.setAttribute("id", trans.scriptId);
6047             this.head.appendChild(script);
6048
6049             this.trans = trans;
6050         }else{
6051             callback.call(scope||this, null, arg, false);
6052         }
6053     },
6054
6055     // private
6056     isLoading : function(){
6057         return this.trans ? true : false;
6058     },
6059
6060     /**
6061      * Abort the current server request.
6062      */
6063     abort : function(){
6064         if(this.isLoading()){
6065             this.destroyTrans(this.trans);
6066         }
6067     },
6068
6069     // private
6070     destroyTrans : function(trans, isLoaded){
6071         this.head.removeChild(document.getElementById(trans.scriptId));
6072         clearTimeout(trans.timeoutId);
6073         if(isLoaded){
6074             window[trans.cb] = undefined;
6075             try{
6076                 delete window[trans.cb];
6077             }catch(e){}
6078         }else{
6079             // if hasn't been loaded, wait for load to remove it to prevent script error
6080             window[trans.cb] = function(){
6081                 window[trans.cb] = undefined;
6082                 try{
6083                     delete window[trans.cb];
6084                 }catch(e){}
6085             };
6086         }
6087     },
6088
6089     // private
6090     handleResponse : function(o, trans){
6091         this.trans = false;
6092         this.destroyTrans(trans, true);
6093         var result;
6094         try {
6095             result = trans.reader.readRecords(o);
6096         }catch(e){
6097             this.fireEvent("loadexception", this, o, trans.arg, e);
6098             trans.callback.call(trans.scope||window, null, trans.arg, false);
6099             return;
6100         }
6101         this.fireEvent("load", this, o, trans.arg);
6102         trans.callback.call(trans.scope||window, result, trans.arg, true);
6103     },
6104
6105     // private
6106     handleFailure : function(trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, false);
6109         this.fireEvent("loadexception", this, null, trans.arg);
6110         trans.callback.call(trans.scope||window, null, trans.arg, false);
6111     }
6112 });/*
6113  * Based on:
6114  * Ext JS Library 1.1.1
6115  * Copyright(c) 2006-2007, Ext JS, LLC.
6116  *
6117  * Originally Released Under LGPL - original licence link has changed is not relivant.
6118  *
6119  * Fork - LGPL
6120  * <script type="text/javascript">
6121  */
6122
6123 /**
6124  * @class Roo.data.JsonReader
6125  * @extends Roo.data.DataReader
6126  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6127  * based on mappings in a provided Roo.data.Record constructor.
6128  * 
6129  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6130  * in the reply previously. 
6131  * 
6132  * <p>
6133  * Example code:
6134  * <pre><code>
6135 var RecordDef = Roo.data.Record.create([
6136     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6137     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6138 ]);
6139 var myReader = new Roo.data.JsonReader({
6140     totalProperty: "results",    // The property which contains the total dataset size (optional)
6141     root: "rows",                // The property which contains an Array of row objects
6142     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6143 }, RecordDef);
6144 </code></pre>
6145  * <p>
6146  * This would consume a JSON file like this:
6147  * <pre><code>
6148 { 'results': 2, 'rows': [
6149     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6150     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6151 }
6152 </code></pre>
6153  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6154  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6155  * paged from the remote server.
6156  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6157  * @cfg {String} root name of the property which contains the Array of row objects.
6158  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6159  * @constructor
6160  * Create a new JsonReader
6161  * @param {Object} meta Metadata configuration options
6162  * @param {Object} recordType Either an Array of field definition objects,
6163  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6164  */
6165 Roo.data.JsonReader = function(meta, recordType){
6166     
6167     meta = meta || {};
6168     // set some defaults:
6169     Roo.applyIf(meta, {
6170         totalProperty: 'total',
6171         successProperty : 'success',
6172         root : 'data',
6173         id : 'id'
6174     });
6175     
6176     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6177 };
6178 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6179     
6180     /**
6181      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6182      * Used by Store query builder to append _requestMeta to params.
6183      * 
6184      */
6185     metaFromRemote : false,
6186     /**
6187      * This method is only used by a DataProxy which has retrieved data from a remote server.
6188      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6189      * @return {Object} data A data block which is used by an Roo.data.Store object as
6190      * a cache of Roo.data.Records.
6191      */
6192     read : function(response){
6193         var json = response.responseText;
6194        
6195         var o = /* eval:var:o */ eval("("+json+")");
6196         if(!o) {
6197             throw {message: "JsonReader.read: Json object not found"};
6198         }
6199         
6200         if(o.metaData){
6201             
6202             delete this.ef;
6203             this.metaFromRemote = true;
6204             this.meta = o.metaData;
6205             this.recordType = Roo.data.Record.create(o.metaData.fields);
6206             this.onMetaChange(this.meta, this.recordType, o);
6207         }
6208         return this.readRecords(o);
6209     },
6210
6211     // private function a store will implement
6212     onMetaChange : function(meta, recordType, o){
6213
6214     },
6215
6216     /**
6217          * @ignore
6218          */
6219     simpleAccess: function(obj, subsc) {
6220         return obj[subsc];
6221     },
6222
6223         /**
6224          * @ignore
6225          */
6226     getJsonAccessor: function(){
6227         var re = /[\[\.]/;
6228         return function(expr) {
6229             try {
6230                 return(re.test(expr))
6231                     ? new Function("obj", "return obj." + expr)
6232                     : function(obj){
6233                         return obj[expr];
6234                     };
6235             } catch(e){}
6236             return Roo.emptyFn;
6237         };
6238     }(),
6239
6240     /**
6241      * Create a data block containing Roo.data.Records from an XML document.
6242      * @param {Object} o An object which contains an Array of row objects in the property specified
6243      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6244      * which contains the total size of the dataset.
6245      * @return {Object} data A data block which is used by an Roo.data.Store object as
6246      * a cache of Roo.data.Records.
6247      */
6248     readRecords : function(o){
6249         /**
6250          * After any data loads, the raw JSON data is available for further custom processing.
6251          * @type Object
6252          */
6253         this.o = o;
6254         var s = this.meta, Record = this.recordType,
6255             f = Record.prototype.fields, fi = f.items, fl = f.length;
6256
6257 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6258         if (!this.ef) {
6259             if(s.totalProperty) {
6260                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6261                 }
6262                 if(s.successProperty) {
6263                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6264                 }
6265                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6266                 if (s.id) {
6267                         var g = this.getJsonAccessor(s.id);
6268                         this.getId = function(rec) {
6269                                 var r = g(rec);
6270                                 return (r === undefined || r === "") ? null : r;
6271                         };
6272                 } else {
6273                         this.getId = function(){return null;};
6274                 }
6275             this.ef = [];
6276             for(var jj = 0; jj < fl; jj++){
6277                 f = fi[jj];
6278                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6279                 this.ef[jj] = this.getJsonAccessor(map);
6280             }
6281         }
6282
6283         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6284         if(s.totalProperty){
6285             var vt = parseInt(this.getTotal(o), 10);
6286             if(!isNaN(vt)){
6287                 totalRecords = vt;
6288             }
6289         }
6290         if(s.successProperty){
6291             var vs = this.getSuccess(o);
6292             if(vs === false || vs === 'false'){
6293                 success = false;
6294             }
6295         }
6296         var records = [];
6297             for(var i = 0; i < c; i++){
6298                     var n = root[i];
6299                 var values = {};
6300                 var id = this.getId(n);
6301                 for(var j = 0; j < fl; j++){
6302                     f = fi[j];
6303                 var v = this.ef[j](n);
6304                 if (!f.convert) {
6305                     Roo.log('missing convert for ' + f.name);
6306                     Roo.log(f);
6307                     continue;
6308                 }
6309                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6310                 }
6311                 var record = new Record(values, id);
6312                 record.json = n;
6313                 records[i] = record;
6314             }
6315             return {
6316             raw : o,
6317                 success : success,
6318                 records : records,
6319                 totalRecords : totalRecords
6320             };
6321     }
6322 });/*
6323  * Based on:
6324  * Ext JS Library 1.1.1
6325  * Copyright(c) 2006-2007, Ext JS, LLC.
6326  *
6327  * Originally Released Under LGPL - original licence link has changed is not relivant.
6328  *
6329  * Fork - LGPL
6330  * <script type="text/javascript">
6331  */
6332
6333 /**
6334  * @class Roo.data.XmlReader
6335  * @extends Roo.data.DataReader
6336  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6337  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6338  * <p>
6339  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6340  * header in the HTTP response must be set to "text/xml".</em>
6341  * <p>
6342  * Example code:
6343  * <pre><code>
6344 var RecordDef = Roo.data.Record.create([
6345    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6346    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6347 ]);
6348 var myReader = new Roo.data.XmlReader({
6349    totalRecords: "results", // The element which contains the total dataset size (optional)
6350    record: "row",           // The repeated element which contains row information
6351    id: "id"                 // The element within the row that provides an ID for the record (optional)
6352 }, RecordDef);
6353 </code></pre>
6354  * <p>
6355  * This would consume an XML file like this:
6356  * <pre><code>
6357 &lt;?xml?>
6358 &lt;dataset>
6359  &lt;results>2&lt;/results>
6360  &lt;row>
6361    &lt;id>1&lt;/id>
6362    &lt;name>Bill&lt;/name>
6363    &lt;occupation>Gardener&lt;/occupation>
6364  &lt;/row>
6365  &lt;row>
6366    &lt;id>2&lt;/id>
6367    &lt;name>Ben&lt;/name>
6368    &lt;occupation>Horticulturalist&lt;/occupation>
6369  &lt;/row>
6370 &lt;/dataset>
6371 </code></pre>
6372  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6373  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6374  * paged from the remote server.
6375  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6376  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6377  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6378  * a record identifier value.
6379  * @constructor
6380  * Create a new XmlReader
6381  * @param {Object} meta Metadata configuration options
6382  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6383  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6384  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6385  */
6386 Roo.data.XmlReader = function(meta, recordType){
6387     meta = meta || {};
6388     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6389 };
6390 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6391     /**
6392      * This method is only used by a DataProxy which has retrieved data from a remote server.
6393          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6394          * to contain a method called 'responseXML' that returns an XML document object.
6395      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6396      * a cache of Roo.data.Records.
6397      */
6398     read : function(response){
6399         var doc = response.responseXML;
6400         if(!doc) {
6401             throw {message: "XmlReader.read: XML Document not available"};
6402         }
6403         return this.readRecords(doc);
6404     },
6405
6406     /**
6407      * Create a data block containing Roo.data.Records from an XML document.
6408          * @param {Object} doc A parsed XML document.
6409      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6410      * a cache of Roo.data.Records.
6411      */
6412     readRecords : function(doc){
6413         /**
6414          * After any data loads/reads, the raw XML Document is available for further custom processing.
6415          * @type XMLDocument
6416          */
6417         this.xmlData = doc;
6418         var root = doc.documentElement || doc;
6419         var q = Roo.DomQuery;
6420         var recordType = this.recordType, fields = recordType.prototype.fields;
6421         var sid = this.meta.id;
6422         var totalRecords = 0, success = true;
6423         if(this.meta.totalRecords){
6424             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6425         }
6426         
6427         if(this.meta.success){
6428             var sv = q.selectValue(this.meta.success, root, true);
6429             success = sv !== false && sv !== 'false';
6430         }
6431         var records = [];
6432         var ns = q.select(this.meta.record, root);
6433         for(var i = 0, len = ns.length; i < len; i++) {
6434                 var n = ns[i];
6435                 var values = {};
6436                 var id = sid ? q.selectValue(sid, n) : undefined;
6437                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6438                     var f = fields.items[j];
6439                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6440                     v = f.convert(v);
6441                     values[f.name] = v;
6442                 }
6443                 var record = new recordType(values, id);
6444                 record.node = n;
6445                 records[records.length] = record;
6446             }
6447
6448             return {
6449                 success : success,
6450                 records : records,
6451                 totalRecords : totalRecords || records.length
6452             };
6453     }
6454 });/*
6455  * Based on:
6456  * Ext JS Library 1.1.1
6457  * Copyright(c) 2006-2007, Ext JS, LLC.
6458  *
6459  * Originally Released Under LGPL - original licence link has changed is not relivant.
6460  *
6461  * Fork - LGPL
6462  * <script type="text/javascript">
6463  */
6464
6465 /**
6466  * @class Roo.data.ArrayReader
6467  * @extends Roo.data.DataReader
6468  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6469  * Each element of that Array represents a row of data fields. The
6470  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6471  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6472  * <p>
6473  * Example code:.
6474  * <pre><code>
6475 var RecordDef = Roo.data.Record.create([
6476     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6477     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6478 ]);
6479 var myReader = new Roo.data.ArrayReader({
6480     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6481 }, RecordDef);
6482 </code></pre>
6483  * <p>
6484  * This would consume an Array like this:
6485  * <pre><code>
6486 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6487   </code></pre>
6488  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6489  * @constructor
6490  * Create a new JsonReader
6491  * @param {Object} meta Metadata configuration options.
6492  * @param {Object} recordType Either an Array of field definition objects
6493  * as specified to {@link Roo.data.Record#create},
6494  * or an {@link Roo.data.Record} object
6495  * created using {@link Roo.data.Record#create}.
6496  */
6497 Roo.data.ArrayReader = function(meta, recordType){
6498     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6499 };
6500
6501 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6502     /**
6503      * Create a data block containing Roo.data.Records from an XML document.
6504      * @param {Object} o An Array of row objects which represents the dataset.
6505      * @return {Object} data A data block which is used by an Roo.data.Store object as
6506      * a cache of Roo.data.Records.
6507      */
6508     readRecords : function(o){
6509         var sid = this.meta ? this.meta.id : null;
6510         var recordType = this.recordType, fields = recordType.prototype.fields;
6511         var records = [];
6512         var root = o;
6513             for(var i = 0; i < root.length; i++){
6514                     var n = root[i];
6515                 var values = {};
6516                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6517                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6518                 var f = fields.items[j];
6519                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6520                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6521                 v = f.convert(v);
6522                 values[f.name] = v;
6523             }
6524                 var record = new recordType(values, id);
6525                 record.json = n;
6526                 records[records.length] = record;
6527             }
6528             return {
6529                 records : records,
6530                 totalRecords : records.length
6531             };
6532     }
6533 });/*
6534  * Based on:
6535  * Ext JS Library 1.1.1
6536  * Copyright(c) 2006-2007, Ext JS, LLC.
6537  *
6538  * Originally Released Under LGPL - original licence link has changed is not relivant.
6539  *
6540  * Fork - LGPL
6541  * <script type="text/javascript">
6542  */
6543
6544
6545 /**
6546  * @class Roo.data.Tree
6547  * @extends Roo.util.Observable
6548  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6549  * in the tree have most standard DOM functionality.
6550  * @constructor
6551  * @param {Node} root (optional) The root node
6552  */
6553 Roo.data.Tree = function(root){
6554    this.nodeHash = {};
6555    /**
6556     * The root node for this tree
6557     * @type Node
6558     */
6559    this.root = null;
6560    if(root){
6561        this.setRootNode(root);
6562    }
6563    this.addEvents({
6564        /**
6565         * @event append
6566         * Fires when a new child node is appended to a node in this tree.
6567         * @param {Tree} tree The owner tree
6568         * @param {Node} parent The parent node
6569         * @param {Node} node The newly appended node
6570         * @param {Number} index The index of the newly appended node
6571         */
6572        "append" : true,
6573        /**
6574         * @event remove
6575         * Fires when a child node is removed from a node in this tree.
6576         * @param {Tree} tree The owner tree
6577         * @param {Node} parent The parent node
6578         * @param {Node} node The child node removed
6579         */
6580        "remove" : true,
6581        /**
6582         * @event move
6583         * Fires when a node is moved to a new location in the tree
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} node The node moved
6586         * @param {Node} oldParent The old parent of this node
6587         * @param {Node} newParent The new parent of this node
6588         * @param {Number} index The index it was moved to
6589         */
6590        "move" : true,
6591        /**
6592         * @event insert
6593         * Fires when a new child node is inserted in a node in this tree.
6594         * @param {Tree} tree The owner tree
6595         * @param {Node} parent The parent node
6596         * @param {Node} node The child node inserted
6597         * @param {Node} refNode The child node the node was inserted before
6598         */
6599        "insert" : true,
6600        /**
6601         * @event beforeappend
6602         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6603         * @param {Tree} tree The owner tree
6604         * @param {Node} parent The parent node
6605         * @param {Node} node The child node to be appended
6606         */
6607        "beforeappend" : true,
6608        /**
6609         * @event beforeremove
6610         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node to be removed
6614         */
6615        "beforeremove" : true,
6616        /**
6617         * @event beforemove
6618         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} node The node being moved
6621         * @param {Node} oldParent The parent of the node
6622         * @param {Node} newParent The new parent the node is moving to
6623         * @param {Number} index The index it is being moved to
6624         */
6625        "beforemove" : true,
6626        /**
6627         * @event beforeinsert
6628         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6629         * @param {Tree} tree The owner tree
6630         * @param {Node} parent The parent node
6631         * @param {Node} node The child node to be inserted
6632         * @param {Node} refNode The child node the node is being inserted before
6633         */
6634        "beforeinsert" : true
6635    });
6636
6637     Roo.data.Tree.superclass.constructor.call(this);
6638 };
6639
6640 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6641     pathSeparator: "/",
6642
6643     proxyNodeEvent : function(){
6644         return this.fireEvent.apply(this, arguments);
6645     },
6646
6647     /**
6648      * Returns the root node for this tree.
6649      * @return {Node}
6650      */
6651     getRootNode : function(){
6652         return this.root;
6653     },
6654
6655     /**
6656      * Sets the root node for this tree.
6657      * @param {Node} node
6658      * @return {Node}
6659      */
6660     setRootNode : function(node){
6661         this.root = node;
6662         node.ownerTree = this;
6663         node.isRoot = true;
6664         this.registerNode(node);
6665         return node;
6666     },
6667
6668     /**
6669      * Gets a node in this tree by its id.
6670      * @param {String} id
6671      * @return {Node}
6672      */
6673     getNodeById : function(id){
6674         return this.nodeHash[id];
6675     },
6676
6677     registerNode : function(node){
6678         this.nodeHash[node.id] = node;
6679     },
6680
6681     unregisterNode : function(node){
6682         delete this.nodeHash[node.id];
6683     },
6684
6685     toString : function(){
6686         return "[Tree"+(this.id?" "+this.id:"")+"]";
6687     }
6688 });
6689
6690 /**
6691  * @class Roo.data.Node
6692  * @extends Roo.util.Observable
6693  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6694  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6695  * @constructor
6696  * @param {Object} attributes The attributes/config for the node
6697  */
6698 Roo.data.Node = function(attributes){
6699     /**
6700      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6701      * @type {Object}
6702      */
6703     this.attributes = attributes || {};
6704     this.leaf = this.attributes.leaf;
6705     /**
6706      * The node id. @type String
6707      */
6708     this.id = this.attributes.id;
6709     if(!this.id){
6710         this.id = Roo.id(null, "ynode-");
6711         this.attributes.id = this.id;
6712     }
6713      
6714     
6715     /**
6716      * All child nodes of this node. @type Array
6717      */
6718     this.childNodes = [];
6719     if(!this.childNodes.indexOf){ // indexOf is a must
6720         this.childNodes.indexOf = function(o){
6721             for(var i = 0, len = this.length; i < len; i++){
6722                 if(this[i] == o) {
6723                     return i;
6724                 }
6725             }
6726             return -1;
6727         };
6728     }
6729     /**
6730      * The parent node for this node. @type Node
6731      */
6732     this.parentNode = null;
6733     /**
6734      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6735      */
6736     this.firstChild = null;
6737     /**
6738      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6739      */
6740     this.lastChild = null;
6741     /**
6742      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6743      */
6744     this.previousSibling = null;
6745     /**
6746      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6747      */
6748     this.nextSibling = null;
6749
6750     this.addEvents({
6751        /**
6752         * @event append
6753         * Fires when a new child node is appended
6754         * @param {Tree} tree The owner tree
6755         * @param {Node} this This node
6756         * @param {Node} node The newly appended node
6757         * @param {Number} index The index of the newly appended node
6758         */
6759        "append" : true,
6760        /**
6761         * @event remove
6762         * Fires when a child node is removed
6763         * @param {Tree} tree The owner tree
6764         * @param {Node} this This node
6765         * @param {Node} node The removed node
6766         */
6767        "remove" : true,
6768        /**
6769         * @event move
6770         * Fires when this node is moved to a new location in the tree
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} oldParent The old parent of this node
6774         * @param {Node} newParent The new parent of this node
6775         * @param {Number} index The index it was moved to
6776         */
6777        "move" : true,
6778        /**
6779         * @event insert
6780         * Fires when a new child node is inserted.
6781         * @param {Tree} tree The owner tree
6782         * @param {Node} this This node
6783         * @param {Node} node The child node inserted
6784         * @param {Node} refNode The child node the node was inserted before
6785         */
6786        "insert" : true,
6787        /**
6788         * @event beforeappend
6789         * Fires before a new child is appended, return false to cancel the append.
6790         * @param {Tree} tree The owner tree
6791         * @param {Node} this This node
6792         * @param {Node} node The child node to be appended
6793         */
6794        "beforeappend" : true,
6795        /**
6796         * @event beforeremove
6797         * Fires before a child is removed, return false to cancel the remove.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node to be removed
6801         */
6802        "beforeremove" : true,
6803        /**
6804         * @event beforemove
6805         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6806         * @param {Tree} tree The owner tree
6807         * @param {Node} this This node
6808         * @param {Node} oldParent The parent of this node
6809         * @param {Node} newParent The new parent this node is moving to
6810         * @param {Number} index The index it is being moved to
6811         */
6812        "beforemove" : true,
6813        /**
6814         * @event beforeinsert
6815         * Fires before a new child is inserted, return false to cancel the insert.
6816         * @param {Tree} tree The owner tree
6817         * @param {Node} this This node
6818         * @param {Node} node The child node to be inserted
6819         * @param {Node} refNode The child node the node is being inserted before
6820         */
6821        "beforeinsert" : true
6822    });
6823     this.listeners = this.attributes.listeners;
6824     Roo.data.Node.superclass.constructor.call(this);
6825 };
6826
6827 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6828     fireEvent : function(evtName){
6829         // first do standard event for this node
6830         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6831             return false;
6832         }
6833         // then bubble it up to the tree if the event wasn't cancelled
6834         var ot = this.getOwnerTree();
6835         if(ot){
6836             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6837                 return false;
6838             }
6839         }
6840         return true;
6841     },
6842
6843     /**
6844      * Returns true if this node is a leaf
6845      * @return {Boolean}
6846      */
6847     isLeaf : function(){
6848         return this.leaf === true;
6849     },
6850
6851     // private
6852     setFirstChild : function(node){
6853         this.firstChild = node;
6854     },
6855
6856     //private
6857     setLastChild : function(node){
6858         this.lastChild = node;
6859     },
6860
6861
6862     /**
6863      * Returns true if this node is the last child of its parent
6864      * @return {Boolean}
6865      */
6866     isLast : function(){
6867        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6868     },
6869
6870     /**
6871      * Returns true if this node is the first child of its parent
6872      * @return {Boolean}
6873      */
6874     isFirst : function(){
6875        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6876     },
6877
6878     hasChildNodes : function(){
6879         return !this.isLeaf() && this.childNodes.length > 0;
6880     },
6881
6882     /**
6883      * Insert node(s) as the last child node of this node.
6884      * @param {Node/Array} node The node or Array of nodes to append
6885      * @return {Node} The appended node if single append, or null if an array was passed
6886      */
6887     appendChild : function(node){
6888         var multi = false;
6889         if(node instanceof Array){
6890             multi = node;
6891         }else if(arguments.length > 1){
6892             multi = arguments;
6893         }
6894         // if passed an array or multiple args do them one by one
6895         if(multi){
6896             for(var i = 0, len = multi.length; i < len; i++) {
6897                 this.appendChild(multi[i]);
6898             }
6899         }else{
6900             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6901                 return false;
6902             }
6903             var index = this.childNodes.length;
6904             var oldParent = node.parentNode;
6905             // it's a move, make sure we move it cleanly
6906             if(oldParent){
6907                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6908                     return false;
6909                 }
6910                 oldParent.removeChild(node);
6911             }
6912             index = this.childNodes.length;
6913             if(index == 0){
6914                 this.setFirstChild(node);
6915             }
6916             this.childNodes.push(node);
6917             node.parentNode = this;
6918             var ps = this.childNodes[index-1];
6919             if(ps){
6920                 node.previousSibling = ps;
6921                 ps.nextSibling = node;
6922             }else{
6923                 node.previousSibling = null;
6924             }
6925             node.nextSibling = null;
6926             this.setLastChild(node);
6927             node.setOwnerTree(this.getOwnerTree());
6928             this.fireEvent("append", this.ownerTree, this, node, index);
6929             if(oldParent){
6930                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6931             }
6932             return node;
6933         }
6934     },
6935
6936     /**
6937      * Removes a child node from this node.
6938      * @param {Node} node The node to remove
6939      * @return {Node} The removed node
6940      */
6941     removeChild : function(node){
6942         var index = this.childNodes.indexOf(node);
6943         if(index == -1){
6944             return false;
6945         }
6946         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6947             return false;
6948         }
6949
6950         // remove it from childNodes collection
6951         this.childNodes.splice(index, 1);
6952
6953         // update siblings
6954         if(node.previousSibling){
6955             node.previousSibling.nextSibling = node.nextSibling;
6956         }
6957         if(node.nextSibling){
6958             node.nextSibling.previousSibling = node.previousSibling;
6959         }
6960
6961         // update child refs
6962         if(this.firstChild == node){
6963             this.setFirstChild(node.nextSibling);
6964         }
6965         if(this.lastChild == node){
6966             this.setLastChild(node.previousSibling);
6967         }
6968
6969         node.setOwnerTree(null);
6970         // clear any references from the node
6971         node.parentNode = null;
6972         node.previousSibling = null;
6973         node.nextSibling = null;
6974         this.fireEvent("remove", this.ownerTree, this, node);
6975         return node;
6976     },
6977
6978     /**
6979      * Inserts the first node before the second node in this nodes childNodes collection.
6980      * @param {Node} node The node to insert
6981      * @param {Node} refNode The node to insert before (if null the node is appended)
6982      * @return {Node} The inserted node
6983      */
6984     insertBefore : function(node, refNode){
6985         if(!refNode){ // like standard Dom, refNode can be null for append
6986             return this.appendChild(node);
6987         }
6988         // nothing to do
6989         if(node == refNode){
6990             return false;
6991         }
6992
6993         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6994             return false;
6995         }
6996         var index = this.childNodes.indexOf(refNode);
6997         var oldParent = node.parentNode;
6998         var refIndex = index;
6999
7000         // when moving internally, indexes will change after remove
7001         if(oldParent == this && this.childNodes.indexOf(node) < index){
7002             refIndex--;
7003         }
7004
7005         // it's a move, make sure we move it cleanly
7006         if(oldParent){
7007             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7008                 return false;
7009             }
7010             oldParent.removeChild(node);
7011         }
7012         if(refIndex == 0){
7013             this.setFirstChild(node);
7014         }
7015         this.childNodes.splice(refIndex, 0, node);
7016         node.parentNode = this;
7017         var ps = this.childNodes[refIndex-1];
7018         if(ps){
7019             node.previousSibling = ps;
7020             ps.nextSibling = node;
7021         }else{
7022             node.previousSibling = null;
7023         }
7024         node.nextSibling = refNode;
7025         refNode.previousSibling = node;
7026         node.setOwnerTree(this.getOwnerTree());
7027         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7028         if(oldParent){
7029             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7030         }
7031         return node;
7032     },
7033
7034     /**
7035      * Returns the child node at the specified index.
7036      * @param {Number} index
7037      * @return {Node}
7038      */
7039     item : function(index){
7040         return this.childNodes[index];
7041     },
7042
7043     /**
7044      * Replaces one child node in this node with another.
7045      * @param {Node} newChild The replacement node
7046      * @param {Node} oldChild The node to replace
7047      * @return {Node} The replaced node
7048      */
7049     replaceChild : function(newChild, oldChild){
7050         this.insertBefore(newChild, oldChild);
7051         this.removeChild(oldChild);
7052         return oldChild;
7053     },
7054
7055     /**
7056      * Returns the index of a child node
7057      * @param {Node} node
7058      * @return {Number} The index of the node or -1 if it was not found
7059      */
7060     indexOf : function(child){
7061         return this.childNodes.indexOf(child);
7062     },
7063
7064     /**
7065      * Returns the tree this node is in.
7066      * @return {Tree}
7067      */
7068     getOwnerTree : function(){
7069         // if it doesn't have one, look for one
7070         if(!this.ownerTree){
7071             var p = this;
7072             while(p){
7073                 if(p.ownerTree){
7074                     this.ownerTree = p.ownerTree;
7075                     break;
7076                 }
7077                 p = p.parentNode;
7078             }
7079         }
7080         return this.ownerTree;
7081     },
7082
7083     /**
7084      * Returns depth of this node (the root node has a depth of 0)
7085      * @return {Number}
7086      */
7087     getDepth : function(){
7088         var depth = 0;
7089         var p = this;
7090         while(p.parentNode){
7091             ++depth;
7092             p = p.parentNode;
7093         }
7094         return depth;
7095     },
7096
7097     // private
7098     setOwnerTree : function(tree){
7099         // if it's move, we need to update everyone
7100         if(tree != this.ownerTree){
7101             if(this.ownerTree){
7102                 this.ownerTree.unregisterNode(this);
7103             }
7104             this.ownerTree = tree;
7105             var cs = this.childNodes;
7106             for(var i = 0, len = cs.length; i < len; i++) {
7107                 cs[i].setOwnerTree(tree);
7108             }
7109             if(tree){
7110                 tree.registerNode(this);
7111             }
7112         }
7113     },
7114
7115     /**
7116      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7117      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7118      * @return {String} The path
7119      */
7120     getPath : function(attr){
7121         attr = attr || "id";
7122         var p = this.parentNode;
7123         var b = [this.attributes[attr]];
7124         while(p){
7125             b.unshift(p.attributes[attr]);
7126             p = p.parentNode;
7127         }
7128         var sep = this.getOwnerTree().pathSeparator;
7129         return sep + b.join(sep);
7130     },
7131
7132     /**
7133      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the bubble is stopped.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     bubble : function(fn, scope, args){
7142         var p = this;
7143         while(p){
7144             if(fn.call(scope || p, args || p) === false){
7145                 break;
7146             }
7147             p = p.parentNode;
7148         }
7149     },
7150
7151     /**
7152      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7153      * function call will be the scope provided or the current node. The arguments to the function
7154      * will be the args provided or the current node. If the function returns false at any point,
7155      * the cascade is stopped on that branch.
7156      * @param {Function} fn The function to call
7157      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7158      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7159      */
7160     cascade : function(fn, scope, args){
7161         if(fn.call(scope || this, args || this) !== false){
7162             var cs = this.childNodes;
7163             for(var i = 0, len = cs.length; i < len; i++) {
7164                 cs[i].cascade(fn, scope, args);
7165             }
7166         }
7167     },
7168
7169     /**
7170      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7171      * function call will be the scope provided or the current node. The arguments to the function
7172      * will be the args provided or the current node. If the function returns false at any point,
7173      * the iteration stops.
7174      * @param {Function} fn The function to call
7175      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7176      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7177      */
7178     eachChild : function(fn, scope, args){
7179         var cs = this.childNodes;
7180         for(var i = 0, len = cs.length; i < len; i++) {
7181                 if(fn.call(scope || this, args || cs[i]) === false){
7182                     break;
7183                 }
7184         }
7185     },
7186
7187     /**
7188      * Finds the first child that has the attribute with the specified value.
7189      * @param {String} attribute The attribute name
7190      * @param {Mixed} value The value to search for
7191      * @return {Node} The found child or null if none was found
7192      */
7193     findChild : function(attribute, value){
7194         var cs = this.childNodes;
7195         for(var i = 0, len = cs.length; i < len; i++) {
7196                 if(cs[i].attributes[attribute] == value){
7197                     return cs[i];
7198                 }
7199         }
7200         return null;
7201     },
7202
7203     /**
7204      * Finds the first child by a custom function. The child matches if the function passed
7205      * returns true.
7206      * @param {Function} fn
7207      * @param {Object} scope (optional)
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChildBy : function(fn, scope){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(fn.call(scope||cs[i], cs[i]) === true){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Sorts this nodes children using the supplied sort function
7222      * @param {Function} fn
7223      * @param {Object} scope (optional)
7224      */
7225     sort : function(fn, scope){
7226         var cs = this.childNodes;
7227         var len = cs.length;
7228         if(len > 0){
7229             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7230             cs.sort(sortFn);
7231             for(var i = 0; i < len; i++){
7232                 var n = cs[i];
7233                 n.previousSibling = cs[i-1];
7234                 n.nextSibling = cs[i+1];
7235                 if(i == 0){
7236                     this.setFirstChild(n);
7237                 }
7238                 if(i == len-1){
7239                     this.setLastChild(n);
7240                 }
7241             }
7242         }
7243     },
7244
7245     /**
7246      * Returns true if this node is an ancestor (at any point) of the passed node.
7247      * @param {Node} node
7248      * @return {Boolean}
7249      */
7250     contains : function(node){
7251         return node.isAncestor(this);
7252     },
7253
7254     /**
7255      * Returns true if the passed node is an ancestor (at any point) of this node.
7256      * @param {Node} node
7257      * @return {Boolean}
7258      */
7259     isAncestor : function(node){
7260         var p = this.parentNode;
7261         while(p){
7262             if(p == node){
7263                 return true;
7264             }
7265             p = p.parentNode;
7266         }
7267         return false;
7268     },
7269
7270     toString : function(){
7271         return "[Node"+(this.id?" "+this.id:"")+"]";
7272     }
7273 });/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284
7285 /**
7286  * @class Roo.ComponentMgr
7287  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7288  * @singleton
7289  */
7290 Roo.ComponentMgr = function(){
7291     var all = new Roo.util.MixedCollection();
7292
7293     return {
7294         /**
7295          * Registers a component.
7296          * @param {Roo.Component} c The component
7297          */
7298         register : function(c){
7299             all.add(c);
7300         },
7301
7302         /**
7303          * Unregisters a component.
7304          * @param {Roo.Component} c The component
7305          */
7306         unregister : function(c){
7307             all.remove(c);
7308         },
7309
7310         /**
7311          * Returns a component by id
7312          * @param {String} id The component id
7313          */
7314         get : function(id){
7315             return all.get(id);
7316         },
7317
7318         /**
7319          * Registers a function that will be called when a specified component is added to ComponentMgr
7320          * @param {String} id The component id
7321          * @param {Funtction} fn The callback function
7322          * @param {Object} scope The scope of the callback
7323          */
7324         onAvailable : function(id, fn, scope){
7325             all.on("add", function(index, o){
7326                 if(o.id == id){
7327                     fn.call(scope || o, o);
7328                     all.un("add", fn, scope);
7329                 }
7330             });
7331         }
7332     };
7333 }();/*
7334  * Based on:
7335  * Ext JS Library 1.1.1
7336  * Copyright(c) 2006-2007, Ext JS, LLC.
7337  *
7338  * Originally Released Under LGPL - original licence link has changed is not relivant.
7339  *
7340  * Fork - LGPL
7341  * <script type="text/javascript">
7342  */
7343  
7344 /**
7345  * @class Roo.Component
7346  * @extends Roo.util.Observable
7347  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7348  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7349  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7350  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7351  * All visual components (widgets) that require rendering into a layout should subclass Component.
7352  * @constructor
7353  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7354  * 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
7355  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7356  */
7357 Roo.Component = function(config){
7358     config = config || {};
7359     if(config.tagName || config.dom || typeof config == "string"){ // element object
7360         config = {el: config, id: config.id || config};
7361     }
7362     this.initialConfig = config;
7363
7364     Roo.apply(this, config);
7365     this.addEvents({
7366         /**
7367          * @event disable
7368          * Fires after the component is disabled.
7369              * @param {Roo.Component} this
7370              */
7371         disable : true,
7372         /**
7373          * @event enable
7374          * Fires after the component is enabled.
7375              * @param {Roo.Component} this
7376              */
7377         enable : true,
7378         /**
7379          * @event beforeshow
7380          * Fires before the component is shown.  Return false to stop the show.
7381              * @param {Roo.Component} this
7382              */
7383         beforeshow : true,
7384         /**
7385          * @event show
7386          * Fires after the component is shown.
7387              * @param {Roo.Component} this
7388              */
7389         show : true,
7390         /**
7391          * @event beforehide
7392          * Fires before the component is hidden. Return false to stop the hide.
7393              * @param {Roo.Component} this
7394              */
7395         beforehide : true,
7396         /**
7397          * @event hide
7398          * Fires after the component is hidden.
7399              * @param {Roo.Component} this
7400              */
7401         hide : true,
7402         /**
7403          * @event beforerender
7404          * Fires before the component is rendered. Return false to stop the render.
7405              * @param {Roo.Component} this
7406              */
7407         beforerender : true,
7408         /**
7409          * @event render
7410          * Fires after the component is rendered.
7411              * @param {Roo.Component} this
7412              */
7413         render : true,
7414         /**
7415          * @event beforedestroy
7416          * Fires before the component is destroyed. Return false to stop the destroy.
7417              * @param {Roo.Component} this
7418              */
7419         beforedestroy : true,
7420         /**
7421          * @event destroy
7422          * Fires after the component is destroyed.
7423              * @param {Roo.Component} this
7424              */
7425         destroy : true
7426     });
7427     if(!this.id){
7428         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7429     }
7430     Roo.ComponentMgr.register(this);
7431     Roo.Component.superclass.constructor.call(this);
7432     this.initComponent();
7433     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7434         this.render(this.renderTo);
7435         delete this.renderTo;
7436     }
7437 };
7438
7439 /** @private */
7440 Roo.Component.AUTO_ID = 1000;
7441
7442 Roo.extend(Roo.Component, Roo.util.Observable, {
7443     /**
7444      * @scope Roo.Component.prototype
7445      * @type {Boolean}
7446      * true if this component is hidden. Read-only.
7447      */
7448     hidden : false,
7449     /**
7450      * @type {Boolean}
7451      * true if this component is disabled. Read-only.
7452      */
7453     disabled : false,
7454     /**
7455      * @type {Boolean}
7456      * true if this component has been rendered. Read-only.
7457      */
7458     rendered : false,
7459     
7460     /** @cfg {String} disableClass
7461      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7462      */
7463     disabledClass : "x-item-disabled",
7464         /** @cfg {Boolean} allowDomMove
7465          * Whether the component can move the Dom node when rendering (defaults to true).
7466          */
7467     allowDomMove : true,
7468     /** @cfg {String} hideMode
7469      * How this component should hidden. Supported values are
7470      * "visibility" (css visibility), "offsets" (negative offset position) and
7471      * "display" (css display) - defaults to "display".
7472      */
7473     hideMode: 'display',
7474
7475     /** @private */
7476     ctype : "Roo.Component",
7477
7478     /**
7479      * @cfg {String} actionMode 
7480      * which property holds the element that used for  hide() / show() / disable() / enable()
7481      * default is 'el' 
7482      */
7483     actionMode : "el",
7484
7485     /** @private */
7486     getActionEl : function(){
7487         return this[this.actionMode];
7488     },
7489
7490     initComponent : Roo.emptyFn,
7491     /**
7492      * If this is a lazy rendering component, render it to its container element.
7493      * @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.
7494      */
7495     render : function(container, position){
7496         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7497             if(!container && this.el){
7498                 this.el = Roo.get(this.el);
7499                 container = this.el.dom.parentNode;
7500                 this.allowDomMove = false;
7501             }
7502             this.container = Roo.get(container);
7503             this.rendered = true;
7504             if(position !== undefined){
7505                 if(typeof position == 'number'){
7506                     position = this.container.dom.childNodes[position];
7507                 }else{
7508                     position = Roo.getDom(position);
7509                 }
7510             }
7511             this.onRender(this.container, position || null);
7512             if(this.cls){
7513                 this.el.addClass(this.cls);
7514                 delete this.cls;
7515             }
7516             if(this.style){
7517                 this.el.applyStyles(this.style);
7518                 delete this.style;
7519             }
7520             this.fireEvent("render", this);
7521             this.afterRender(this.container);
7522             if(this.hidden){
7523                 this.hide();
7524             }
7525             if(this.disabled){
7526                 this.disable();
7527             }
7528         }
7529         return this;
7530     },
7531
7532     /** @private */
7533     // default function is not really useful
7534     onRender : function(ct, position){
7535         if(this.el){
7536             this.el = Roo.get(this.el);
7537             if(this.allowDomMove !== false){
7538                 ct.dom.insertBefore(this.el.dom, position);
7539             }
7540         }
7541     },
7542
7543     /** @private */
7544     getAutoCreate : function(){
7545         var cfg = typeof this.autoCreate == "object" ?
7546                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7547         if(this.id && !cfg.id){
7548             cfg.id = this.id;
7549         }
7550         return cfg;
7551     },
7552
7553     /** @private */
7554     afterRender : Roo.emptyFn,
7555
7556     /**
7557      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7558      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7559      */
7560     destroy : function(){
7561         if(this.fireEvent("beforedestroy", this) !== false){
7562             this.purgeListeners();
7563             this.beforeDestroy();
7564             if(this.rendered){
7565                 this.el.removeAllListeners();
7566                 this.el.remove();
7567                 if(this.actionMode == "container"){
7568                     this.container.remove();
7569                 }
7570             }
7571             this.onDestroy();
7572             Roo.ComponentMgr.unregister(this);
7573             this.fireEvent("destroy", this);
7574         }
7575     },
7576
7577         /** @private */
7578     beforeDestroy : function(){
7579
7580     },
7581
7582         /** @private */
7583         onDestroy : function(){
7584
7585     },
7586
7587     /**
7588      * Returns the underlying {@link Roo.Element}.
7589      * @return {Roo.Element} The element
7590      */
7591     getEl : function(){
7592         return this.el;
7593     },
7594
7595     /**
7596      * Returns the id of this component.
7597      * @return {String}
7598      */
7599     getId : function(){
7600         return this.id;
7601     },
7602
7603     /**
7604      * Try to focus this component.
7605      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7606      * @return {Roo.Component} this
7607      */
7608     focus : function(selectText){
7609         if(this.rendered){
7610             this.el.focus();
7611             if(selectText === true){
7612                 this.el.dom.select();
7613             }
7614         }
7615         return this;
7616     },
7617
7618     /** @private */
7619     blur : function(){
7620         if(this.rendered){
7621             this.el.blur();
7622         }
7623         return this;
7624     },
7625
7626     /**
7627      * Disable this component.
7628      * @return {Roo.Component} this
7629      */
7630     disable : function(){
7631         if(this.rendered){
7632             this.onDisable();
7633         }
7634         this.disabled = true;
7635         this.fireEvent("disable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onDisable : function(){
7641         this.getActionEl().addClass(this.disabledClass);
7642         this.el.dom.disabled = true;
7643     },
7644
7645     /**
7646      * Enable this component.
7647      * @return {Roo.Component} this
7648      */
7649     enable : function(){
7650         if(this.rendered){
7651             this.onEnable();
7652         }
7653         this.disabled = false;
7654         this.fireEvent("enable", this);
7655         return this;
7656     },
7657
7658         // private
7659     onEnable : function(){
7660         this.getActionEl().removeClass(this.disabledClass);
7661         this.el.dom.disabled = false;
7662     },
7663
7664     /**
7665      * Convenience function for setting disabled/enabled by boolean.
7666      * @param {Boolean} disabled
7667      */
7668     setDisabled : function(disabled){
7669         this[disabled ? "disable" : "enable"]();
7670     },
7671
7672     /**
7673      * Show this component.
7674      * @return {Roo.Component} this
7675      */
7676     show: function(){
7677         if(this.fireEvent("beforeshow", this) !== false){
7678             this.hidden = false;
7679             if(this.rendered){
7680                 this.onShow();
7681             }
7682             this.fireEvent("show", this);
7683         }
7684         return this;
7685     },
7686
7687     // private
7688     onShow : function(){
7689         var ae = this.getActionEl();
7690         if(this.hideMode == 'visibility'){
7691             ae.dom.style.visibility = "visible";
7692         }else if(this.hideMode == 'offsets'){
7693             ae.removeClass('x-hidden');
7694         }else{
7695             ae.dom.style.display = "";
7696         }
7697     },
7698
7699     /**
7700      * Hide this component.
7701      * @return {Roo.Component} this
7702      */
7703     hide: function(){
7704         if(this.fireEvent("beforehide", this) !== false){
7705             this.hidden = true;
7706             if(this.rendered){
7707                 this.onHide();
7708             }
7709             this.fireEvent("hide", this);
7710         }
7711         return this;
7712     },
7713
7714     // private
7715     onHide : function(){
7716         var ae = this.getActionEl();
7717         if(this.hideMode == 'visibility'){
7718             ae.dom.style.visibility = "hidden";
7719         }else if(this.hideMode == 'offsets'){
7720             ae.addClass('x-hidden');
7721         }else{
7722             ae.dom.style.display = "none";
7723         }
7724     },
7725
7726     /**
7727      * Convenience function to hide or show this component by boolean.
7728      * @param {Boolean} visible True to show, false to hide
7729      * @return {Roo.Component} this
7730      */
7731     setVisible: function(visible){
7732         if(visible) {
7733             this.show();
7734         }else{
7735             this.hide();
7736         }
7737         return this;
7738     },
7739
7740     /**
7741      * Returns true if this component is visible.
7742      */
7743     isVisible : function(){
7744         return this.getActionEl().isVisible();
7745     },
7746
7747     cloneConfig : function(overrides){
7748         overrides = overrides || {};
7749         var id = overrides.id || Roo.id();
7750         var cfg = Roo.applyIf(overrides, this.initialConfig);
7751         cfg.id = id; // prevent dup id
7752         return new this.constructor(cfg);
7753     }
7754 });/*
7755  * Based on:
7756  * Ext JS Library 1.1.1
7757  * Copyright(c) 2006-2007, Ext JS, LLC.
7758  *
7759  * Originally Released Under LGPL - original licence link has changed is not relivant.
7760  *
7761  * Fork - LGPL
7762  * <script type="text/javascript">
7763  */
7764  (function(){ 
7765 /**
7766  * @class Roo.Layer
7767  * @extends Roo.Element
7768  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7769  * automatic maintaining of shadow/shim positions.
7770  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7771  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7772  * you can pass a string with a CSS class name. False turns off the shadow.
7773  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7774  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7775  * @cfg {String} cls CSS class to add to the element
7776  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7777  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7778  * @constructor
7779  * @param {Object} config An object with config options.
7780  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7781  */
7782
7783 Roo.Layer = function(config, existingEl){
7784     config = config || {};
7785     var dh = Roo.DomHelper;
7786     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7787     if(existingEl){
7788         this.dom = Roo.getDom(existingEl);
7789     }
7790     if(!this.dom){
7791         var o = config.dh || {tag: "div", cls: "x-layer"};
7792         this.dom = dh.append(pel, o);
7793     }
7794     if(config.cls){
7795         this.addClass(config.cls);
7796     }
7797     this.constrain = config.constrain !== false;
7798     this.visibilityMode = Roo.Element.VISIBILITY;
7799     if(config.id){
7800         this.id = this.dom.id = config.id;
7801     }else{
7802         this.id = Roo.id(this.dom);
7803     }
7804     this.zindex = config.zindex || this.getZIndex();
7805     this.position("absolute", this.zindex);
7806     if(config.shadow){
7807         this.shadowOffset = config.shadowOffset || 4;
7808         this.shadow = new Roo.Shadow({
7809             offset : this.shadowOffset,
7810             mode : config.shadow
7811         });
7812     }else{
7813         this.shadowOffset = 0;
7814     }
7815     this.useShim = config.shim !== false && Roo.useShims;
7816     this.useDisplay = config.useDisplay;
7817     this.hide();
7818 };
7819
7820 var supr = Roo.Element.prototype;
7821
7822 // shims are shared among layer to keep from having 100 iframes
7823 var shims = [];
7824
7825 Roo.extend(Roo.Layer, Roo.Element, {
7826
7827     getZIndex : function(){
7828         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7829     },
7830
7831     getShim : function(){
7832         if(!this.useShim){
7833             return null;
7834         }
7835         if(this.shim){
7836             return this.shim;
7837         }
7838         var shim = shims.shift();
7839         if(!shim){
7840             shim = this.createShim();
7841             shim.enableDisplayMode('block');
7842             shim.dom.style.display = 'none';
7843             shim.dom.style.visibility = 'visible';
7844         }
7845         var pn = this.dom.parentNode;
7846         if(shim.dom.parentNode != pn){
7847             pn.insertBefore(shim.dom, this.dom);
7848         }
7849         shim.setStyle('z-index', this.getZIndex()-2);
7850         this.shim = shim;
7851         return shim;
7852     },
7853
7854     hideShim : function(){
7855         if(this.shim){
7856             this.shim.setDisplayed(false);
7857             shims.push(this.shim);
7858             delete this.shim;
7859         }
7860     },
7861
7862     disableShadow : function(){
7863         if(this.shadow){
7864             this.shadowDisabled = true;
7865             this.shadow.hide();
7866             this.lastShadowOffset = this.shadowOffset;
7867             this.shadowOffset = 0;
7868         }
7869     },
7870
7871     enableShadow : function(show){
7872         if(this.shadow){
7873             this.shadowDisabled = false;
7874             this.shadowOffset = this.lastShadowOffset;
7875             delete this.lastShadowOffset;
7876             if(show){
7877                 this.sync(true);
7878             }
7879         }
7880     },
7881
7882     // private
7883     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7884     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7885     sync : function(doShow){
7886         var sw = this.shadow;
7887         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7888             var sh = this.getShim();
7889
7890             var w = this.getWidth(),
7891                 h = this.getHeight();
7892
7893             var l = this.getLeft(true),
7894                 t = this.getTop(true);
7895
7896             if(sw && !this.shadowDisabled){
7897                 if(doShow && !sw.isVisible()){
7898                     sw.show(this);
7899                 }else{
7900                     sw.realign(l, t, w, h);
7901                 }
7902                 if(sh){
7903                     if(doShow){
7904                        sh.show();
7905                     }
7906                     // fit the shim behind the shadow, so it is shimmed too
7907                     var a = sw.adjusts, s = sh.dom.style;
7908                     s.left = (Math.min(l, l+a.l))+"px";
7909                     s.top = (Math.min(t, t+a.t))+"px";
7910                     s.width = (w+a.w)+"px";
7911                     s.height = (h+a.h)+"px";
7912                 }
7913             }else if(sh){
7914                 if(doShow){
7915                    sh.show();
7916                 }
7917                 sh.setSize(w, h);
7918                 sh.setLeftTop(l, t);
7919             }
7920             
7921         }
7922     },
7923
7924     // private
7925     destroy : function(){
7926         this.hideShim();
7927         if(this.shadow){
7928             this.shadow.hide();
7929         }
7930         this.removeAllListeners();
7931         var pn = this.dom.parentNode;
7932         if(pn){
7933             pn.removeChild(this.dom);
7934         }
7935         Roo.Element.uncache(this.id);
7936     },
7937
7938     remove : function(){
7939         this.destroy();
7940     },
7941
7942     // private
7943     beginUpdate : function(){
7944         this.updating = true;
7945     },
7946
7947     // private
7948     endUpdate : function(){
7949         this.updating = false;
7950         this.sync(true);
7951     },
7952
7953     // private
7954     hideUnders : function(negOffset){
7955         if(this.shadow){
7956             this.shadow.hide();
7957         }
7958         this.hideShim();
7959     },
7960
7961     // private
7962     constrainXY : function(){
7963         if(this.constrain){
7964             var vw = Roo.lib.Dom.getViewWidth(),
7965                 vh = Roo.lib.Dom.getViewHeight();
7966             var s = Roo.get(document).getScroll();
7967
7968             var xy = this.getXY();
7969             var x = xy[0], y = xy[1];   
7970             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7971             // only move it if it needs it
7972             var moved = false;
7973             // first validate right/bottom
7974             if((x + w) > vw+s.left){
7975                 x = vw - w - this.shadowOffset;
7976                 moved = true;
7977             }
7978             if((y + h) > vh+s.top){
7979                 y = vh - h - this.shadowOffset;
7980                 moved = true;
7981             }
7982             // then make sure top/left isn't negative
7983             if(x < s.left){
7984                 x = s.left;
7985                 moved = true;
7986             }
7987             if(y < s.top){
7988                 y = s.top;
7989                 moved = true;
7990             }
7991             if(moved){
7992                 if(this.avoidY){
7993                     var ay = this.avoidY;
7994                     if(y <= ay && (y+h) >= ay){
7995                         y = ay-h-5;   
7996                     }
7997                 }
7998                 xy = [x, y];
7999                 this.storeXY(xy);
8000                 supr.setXY.call(this, xy);
8001                 this.sync();
8002             }
8003         }
8004     },
8005
8006     isVisible : function(){
8007         return this.visible;    
8008     },
8009
8010     // private
8011     showAction : function(){
8012         this.visible = true; // track visibility to prevent getStyle calls
8013         if(this.useDisplay === true){
8014             this.setDisplayed("");
8015         }else if(this.lastXY){
8016             supr.setXY.call(this, this.lastXY);
8017         }else if(this.lastLT){
8018             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8019         }
8020     },
8021
8022     // private
8023     hideAction : function(){
8024         this.visible = false;
8025         if(this.useDisplay === true){
8026             this.setDisplayed(false);
8027         }else{
8028             this.setLeftTop(-10000,-10000);
8029         }
8030     },
8031
8032     // overridden Element method
8033     setVisible : function(v, a, d, c, e){
8034         if(v){
8035             this.showAction();
8036         }
8037         if(a && v){
8038             var cb = function(){
8039                 this.sync(true);
8040                 if(c){
8041                     c();
8042                 }
8043             }.createDelegate(this);
8044             supr.setVisible.call(this, true, true, d, cb, e);
8045         }else{
8046             if(!v){
8047                 this.hideUnders(true);
8048             }
8049             var cb = c;
8050             if(a){
8051                 cb = function(){
8052                     this.hideAction();
8053                     if(c){
8054                         c();
8055                     }
8056                 }.createDelegate(this);
8057             }
8058             supr.setVisible.call(this, v, a, d, cb, e);
8059             if(v){
8060                 this.sync(true);
8061             }else if(!a){
8062                 this.hideAction();
8063             }
8064         }
8065     },
8066
8067     storeXY : function(xy){
8068         delete this.lastLT;
8069         this.lastXY = xy;
8070     },
8071
8072     storeLeftTop : function(left, top){
8073         delete this.lastXY;
8074         this.lastLT = [left, top];
8075     },
8076
8077     // private
8078     beforeFx : function(){
8079         this.beforeAction();
8080         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8081     },
8082
8083     // private
8084     afterFx : function(){
8085         Roo.Layer.superclass.afterFx.apply(this, arguments);
8086         this.sync(this.isVisible());
8087     },
8088
8089     // private
8090     beforeAction : function(){
8091         if(!this.updating && this.shadow){
8092             this.shadow.hide();
8093         }
8094     },
8095
8096     // overridden Element method
8097     setLeft : function(left){
8098         this.storeLeftTop(left, this.getTop(true));
8099         supr.setLeft.apply(this, arguments);
8100         this.sync();
8101     },
8102
8103     setTop : function(top){
8104         this.storeLeftTop(this.getLeft(true), top);
8105         supr.setTop.apply(this, arguments);
8106         this.sync();
8107     },
8108
8109     setLeftTop : function(left, top){
8110         this.storeLeftTop(left, top);
8111         supr.setLeftTop.apply(this, arguments);
8112         this.sync();
8113     },
8114
8115     setXY : function(xy, a, d, c, e){
8116         this.fixDisplay();
8117         this.beforeAction();
8118         this.storeXY(xy);
8119         var cb = this.createCB(c);
8120         supr.setXY.call(this, xy, a, d, cb, e);
8121         if(!a){
8122             cb();
8123         }
8124     },
8125
8126     // private
8127     createCB : function(c){
8128         var el = this;
8129         return function(){
8130             el.constrainXY();
8131             el.sync(true);
8132             if(c){
8133                 c();
8134             }
8135         };
8136     },
8137
8138     // overridden Element method
8139     setX : function(x, a, d, c, e){
8140         this.setXY([x, this.getY()], a, d, c, e);
8141     },
8142
8143     // overridden Element method
8144     setY : function(y, a, d, c, e){
8145         this.setXY([this.getX(), y], a, d, c, e);
8146     },
8147
8148     // overridden Element method
8149     setSize : function(w, h, a, d, c, e){
8150         this.beforeAction();
8151         var cb = this.createCB(c);
8152         supr.setSize.call(this, w, h, a, d, cb, e);
8153         if(!a){
8154             cb();
8155         }
8156     },
8157
8158     // overridden Element method
8159     setWidth : function(w, a, d, c, e){
8160         this.beforeAction();
8161         var cb = this.createCB(c);
8162         supr.setWidth.call(this, w, a, d, cb, e);
8163         if(!a){
8164             cb();
8165         }
8166     },
8167
8168     // overridden Element method
8169     setHeight : function(h, a, d, c, e){
8170         this.beforeAction();
8171         var cb = this.createCB(c);
8172         supr.setHeight.call(this, h, a, d, cb, e);
8173         if(!a){
8174             cb();
8175         }
8176     },
8177
8178     // overridden Element method
8179     setBounds : function(x, y, w, h, a, d, c, e){
8180         this.beforeAction();
8181         var cb = this.createCB(c);
8182         if(!a){
8183             this.storeXY([x, y]);
8184             supr.setXY.call(this, [x, y]);
8185             supr.setSize.call(this, w, h, a, d, cb, e);
8186             cb();
8187         }else{
8188             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8189         }
8190         return this;
8191     },
8192     
8193     /**
8194      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8195      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8196      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8197      * @param {Number} zindex The new z-index to set
8198      * @return {this} The Layer
8199      */
8200     setZIndex : function(zindex){
8201         this.zindex = zindex;
8202         this.setStyle("z-index", zindex + 2);
8203         if(this.shadow){
8204             this.shadow.setZIndex(zindex + 1);
8205         }
8206         if(this.shim){
8207             this.shim.setStyle("z-index", zindex);
8208         }
8209     }
8210 });
8211 })();/*
8212  * Based on:
8213  * Ext JS Library 1.1.1
8214  * Copyright(c) 2006-2007, Ext JS, LLC.
8215  *
8216  * Originally Released Under LGPL - original licence link has changed is not relivant.
8217  *
8218  * Fork - LGPL
8219  * <script type="text/javascript">
8220  */
8221
8222
8223 /**
8224  * @class Roo.Shadow
8225  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8226  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8227  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8228  * @constructor
8229  * Create a new Shadow
8230  * @param {Object} config The config object
8231  */
8232 Roo.Shadow = function(config){
8233     Roo.apply(this, config);
8234     if(typeof this.mode != "string"){
8235         this.mode = this.defaultMode;
8236     }
8237     var o = this.offset, a = {h: 0};
8238     var rad = Math.floor(this.offset/2);
8239     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8240         case "drop":
8241             a.w = 0;
8242             a.l = a.t = o;
8243             a.t -= 1;
8244             if(Roo.isIE){
8245                 a.l -= this.offset + rad;
8246                 a.t -= this.offset + rad;
8247                 a.w -= rad;
8248                 a.h -= rad;
8249                 a.t += 1;
8250             }
8251         break;
8252         case "sides":
8253             a.w = (o*2);
8254             a.l = -o;
8255             a.t = o-1;
8256             if(Roo.isIE){
8257                 a.l -= (this.offset - rad);
8258                 a.t -= this.offset + rad;
8259                 a.l += 1;
8260                 a.w -= (this.offset - rad)*2;
8261                 a.w -= rad + 1;
8262                 a.h -= 1;
8263             }
8264         break;
8265         case "frame":
8266             a.w = a.h = (o*2);
8267             a.l = a.t = -o;
8268             a.t += 1;
8269             a.h -= 2;
8270             if(Roo.isIE){
8271                 a.l -= (this.offset - rad);
8272                 a.t -= (this.offset - rad);
8273                 a.l += 1;
8274                 a.w -= (this.offset + rad + 1);
8275                 a.h -= (this.offset + rad);
8276                 a.h += 1;
8277             }
8278         break;
8279     };
8280
8281     this.adjusts = a;
8282 };
8283
8284 Roo.Shadow.prototype = {
8285     /**
8286      * @cfg {String} mode
8287      * The shadow display mode.  Supports the following options:<br />
8288      * sides: Shadow displays on both sides and bottom only<br />
8289      * frame: Shadow displays equally on all four sides<br />
8290      * drop: Traditional bottom-right drop shadow (default)
8291      */
8292     /**
8293      * @cfg {String} offset
8294      * The number of pixels to offset the shadow from the element (defaults to 4)
8295      */
8296     offset: 4,
8297
8298     // private
8299     defaultMode: "drop",
8300
8301     /**
8302      * Displays the shadow under the target element
8303      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8304      */
8305     show : function(target){
8306         target = Roo.get(target);
8307         if(!this.el){
8308             this.el = Roo.Shadow.Pool.pull();
8309             if(this.el.dom.nextSibling != target.dom){
8310                 this.el.insertBefore(target);
8311             }
8312         }
8313         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8314         if(Roo.isIE){
8315             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8316         }
8317         this.realign(
8318             target.getLeft(true),
8319             target.getTop(true),
8320             target.getWidth(),
8321             target.getHeight()
8322         );
8323         this.el.dom.style.display = "block";
8324     },
8325
8326     /**
8327      * Returns true if the shadow is visible, else false
8328      */
8329     isVisible : function(){
8330         return this.el ? true : false;  
8331     },
8332
8333     /**
8334      * Direct alignment when values are already available. Show must be called at least once before
8335      * calling this method to ensure it is initialized.
8336      * @param {Number} left The target element left position
8337      * @param {Number} top The target element top position
8338      * @param {Number} width The target element width
8339      * @param {Number} height The target element height
8340      */
8341     realign : function(l, t, w, h){
8342         if(!this.el){
8343             return;
8344         }
8345         var a = this.adjusts, d = this.el.dom, s = d.style;
8346         var iea = 0;
8347         s.left = (l+a.l)+"px";
8348         s.top = (t+a.t)+"px";
8349         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8350  
8351         if(s.width != sws || s.height != shs){
8352             s.width = sws;
8353             s.height = shs;
8354             if(!Roo.isIE){
8355                 var cn = d.childNodes;
8356                 var sww = Math.max(0, (sw-12))+"px";
8357                 cn[0].childNodes[1].style.width = sww;
8358                 cn[1].childNodes[1].style.width = sww;
8359                 cn[2].childNodes[1].style.width = sww;
8360                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8361             }
8362         }
8363     },
8364
8365     /**
8366      * Hides this shadow
8367      */
8368     hide : function(){
8369         if(this.el){
8370             this.el.dom.style.display = "none";
8371             Roo.Shadow.Pool.push(this.el);
8372             delete this.el;
8373         }
8374     },
8375
8376     /**
8377      * Adjust the z-index of this shadow
8378      * @param {Number} zindex The new z-index
8379      */
8380     setZIndex : function(z){
8381         this.zIndex = z;
8382         if(this.el){
8383             this.el.setStyle("z-index", z);
8384         }
8385     }
8386 };
8387
8388 // Private utility class that manages the internal Shadow cache
8389 Roo.Shadow.Pool = function(){
8390     var p = [];
8391     var markup = Roo.isIE ?
8392                  '<div class="x-ie-shadow"></div>' :
8393                  '<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>';
8394     return {
8395         pull : function(){
8396             var sh = p.shift();
8397             if(!sh){
8398                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8399                 sh.autoBoxAdjust = false;
8400             }
8401             return sh;
8402         },
8403
8404         push : function(sh){
8405             p.push(sh);
8406         }
8407     };
8408 }();/*
8409  * Based on:
8410  * Ext JS Library 1.1.1
8411  * Copyright(c) 2006-2007, Ext JS, LLC.
8412  *
8413  * Originally Released Under LGPL - original licence link has changed is not relivant.
8414  *
8415  * Fork - LGPL
8416  * <script type="text/javascript">
8417  */
8418
8419 /**
8420  * @class Roo.BoxComponent
8421  * @extends Roo.Component
8422  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8423  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8424  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8425  * layout containers.
8426  * @constructor
8427  * @param {Roo.Element/String/Object} config The configuration options.
8428  */
8429 Roo.BoxComponent = function(config){
8430     Roo.Component.call(this, config);
8431     this.addEvents({
8432         /**
8433          * @event resize
8434          * Fires after the component is resized.
8435              * @param {Roo.Component} this
8436              * @param {Number} adjWidth The box-adjusted width that was set
8437              * @param {Number} adjHeight The box-adjusted height that was set
8438              * @param {Number} rawWidth The width that was originally specified
8439              * @param {Number} rawHeight The height that was originally specified
8440              */
8441         resize : true,
8442         /**
8443          * @event move
8444          * Fires after the component is moved.
8445              * @param {Roo.Component} this
8446              * @param {Number} x The new x position
8447              * @param {Number} y The new y position
8448              */
8449         move : true
8450     });
8451 };
8452
8453 Roo.extend(Roo.BoxComponent, Roo.Component, {
8454     // private, set in afterRender to signify that the component has been rendered
8455     boxReady : false,
8456     // private, used to defer height settings to subclasses
8457     deferHeight: false,
8458     /** @cfg {Number} width
8459      * width (optional) size of component
8460      */
8461      /** @cfg {Number} height
8462      * height (optional) size of component
8463      */
8464      
8465     /**
8466      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8467      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8468      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8469      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8470      * @return {Roo.BoxComponent} this
8471      */
8472     setSize : function(w, h){
8473         // support for standard size objects
8474         if(typeof w == 'object'){
8475             h = w.height;
8476             w = w.width;
8477         }
8478         // not rendered
8479         if(!this.boxReady){
8480             this.width = w;
8481             this.height = h;
8482             return this;
8483         }
8484
8485         // prevent recalcs when not needed
8486         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8487             return this;
8488         }
8489         this.lastSize = {width: w, height: h};
8490
8491         var adj = this.adjustSize(w, h);
8492         var aw = adj.width, ah = adj.height;
8493         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8494             var rz = this.getResizeEl();
8495             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8496                 rz.setSize(aw, ah);
8497             }else if(!this.deferHeight && ah !== undefined){
8498                 rz.setHeight(ah);
8499             }else if(aw !== undefined){
8500                 rz.setWidth(aw);
8501             }
8502             this.onResize(aw, ah, w, h);
8503             this.fireEvent('resize', this, aw, ah, w, h);
8504         }
8505         return this;
8506     },
8507
8508     /**
8509      * Gets the current size of the component's underlying element.
8510      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8511      */
8512     getSize : function(){
8513         return this.el.getSize();
8514     },
8515
8516     /**
8517      * Gets the current XY position of the component's underlying element.
8518      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8519      * @return {Array} The XY position of the element (e.g., [100, 200])
8520      */
8521     getPosition : function(local){
8522         if(local === true){
8523             return [this.el.getLeft(true), this.el.getTop(true)];
8524         }
8525         return this.xy || this.el.getXY();
8526     },
8527
8528     /**
8529      * Gets the current box measurements of the component's underlying element.
8530      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8531      * @returns {Object} box An object in the format {x, y, width, height}
8532      */
8533     getBox : function(local){
8534         var s = this.el.getSize();
8535         if(local){
8536             s.x = this.el.getLeft(true);
8537             s.y = this.el.getTop(true);
8538         }else{
8539             var xy = this.xy || this.el.getXY();
8540             s.x = xy[0];
8541             s.y = xy[1];
8542         }
8543         return s;
8544     },
8545
8546     /**
8547      * Sets the current box measurements of the component's underlying element.
8548      * @param {Object} box An object in the format {x, y, width, height}
8549      * @returns {Roo.BoxComponent} this
8550      */
8551     updateBox : function(box){
8552         this.setSize(box.width, box.height);
8553         this.setPagePosition(box.x, box.y);
8554         return this;
8555     },
8556
8557     // protected
8558     getResizeEl : function(){
8559         return this.resizeEl || this.el;
8560     },
8561
8562     // protected
8563     getPositionEl : function(){
8564         return this.positionEl || this.el;
8565     },
8566
8567     /**
8568      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8569      * This method fires the move event.
8570      * @param {Number} left The new left
8571      * @param {Number} top The new top
8572      * @returns {Roo.BoxComponent} this
8573      */
8574     setPosition : function(x, y){
8575         this.x = x;
8576         this.y = y;
8577         if(!this.boxReady){
8578             return this;
8579         }
8580         var adj = this.adjustPosition(x, y);
8581         var ax = adj.x, ay = adj.y;
8582
8583         var el = this.getPositionEl();
8584         if(ax !== undefined || ay !== undefined){
8585             if(ax !== undefined && ay !== undefined){
8586                 el.setLeftTop(ax, ay);
8587             }else if(ax !== undefined){
8588                 el.setLeft(ax);
8589             }else if(ay !== undefined){
8590                 el.setTop(ay);
8591             }
8592             this.onPosition(ax, ay);
8593             this.fireEvent('move', this, ax, ay);
8594         }
8595         return this;
8596     },
8597
8598     /**
8599      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8600      * This method fires the move event.
8601      * @param {Number} x The new x position
8602      * @param {Number} y The new y position
8603      * @returns {Roo.BoxComponent} this
8604      */
8605     setPagePosition : function(x, y){
8606         this.pageX = x;
8607         this.pageY = y;
8608         if(!this.boxReady){
8609             return;
8610         }
8611         if(x === undefined || y === undefined){ // cannot translate undefined points
8612             return;
8613         }
8614         var p = this.el.translatePoints(x, y);
8615         this.setPosition(p.left, p.top);
8616         return this;
8617     },
8618
8619     // private
8620     onRender : function(ct, position){
8621         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8622         if(this.resizeEl){
8623             this.resizeEl = Roo.get(this.resizeEl);
8624         }
8625         if(this.positionEl){
8626             this.positionEl = Roo.get(this.positionEl);
8627         }
8628     },
8629
8630     // private
8631     afterRender : function(){
8632         Roo.BoxComponent.superclass.afterRender.call(this);
8633         this.boxReady = true;
8634         this.setSize(this.width, this.height);
8635         if(this.x || this.y){
8636             this.setPosition(this.x, this.y);
8637         }
8638         if(this.pageX || this.pageY){
8639             this.setPagePosition(this.pageX, this.pageY);
8640         }
8641     },
8642
8643     /**
8644      * Force the component's size to recalculate based on the underlying element's current height and width.
8645      * @returns {Roo.BoxComponent} this
8646      */
8647     syncSize : function(){
8648         delete this.lastSize;
8649         this.setSize(this.el.getWidth(), this.el.getHeight());
8650         return this;
8651     },
8652
8653     /**
8654      * Called after the component is resized, this method is empty by default but can be implemented by any
8655      * subclass that needs to perform custom logic after a resize occurs.
8656      * @param {Number} adjWidth The box-adjusted width that was set
8657      * @param {Number} adjHeight The box-adjusted height that was set
8658      * @param {Number} rawWidth The width that was originally specified
8659      * @param {Number} rawHeight The height that was originally specified
8660      */
8661     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8662
8663     },
8664
8665     /**
8666      * Called after the component is moved, this method is empty by default but can be implemented by any
8667      * subclass that needs to perform custom logic after a move occurs.
8668      * @param {Number} x The new x position
8669      * @param {Number} y The new y position
8670      */
8671     onPosition : function(x, y){
8672
8673     },
8674
8675     // private
8676     adjustSize : function(w, h){
8677         if(this.autoWidth){
8678             w = 'auto';
8679         }
8680         if(this.autoHeight){
8681             h = 'auto';
8682         }
8683         return {width : w, height: h};
8684     },
8685
8686     // private
8687     adjustPosition : function(x, y){
8688         return {x : x, y: y};
8689     }
8690 });/*
8691  * Based on:
8692  * Ext JS Library 1.1.1
8693  * Copyright(c) 2006-2007, Ext JS, LLC.
8694  *
8695  * Originally Released Under LGPL - original licence link has changed is not relivant.
8696  *
8697  * Fork - LGPL
8698  * <script type="text/javascript">
8699  */
8700
8701
8702 /**
8703  * @class Roo.SplitBar
8704  * @extends Roo.util.Observable
8705  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8706  * <br><br>
8707  * Usage:
8708  * <pre><code>
8709 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8710                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8711 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8712 split.minSize = 100;
8713 split.maxSize = 600;
8714 split.animate = true;
8715 split.on('moved', splitterMoved);
8716 </code></pre>
8717  * @constructor
8718  * Create a new SplitBar
8719  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8720  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8721  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8722  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8723                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8724                         position of the SplitBar).
8725  */
8726 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8727     
8728     /** @private */
8729     this.el = Roo.get(dragElement, true);
8730     this.el.dom.unselectable = "on";
8731     /** @private */
8732     this.resizingEl = Roo.get(resizingElement, true);
8733
8734     /**
8735      * @private
8736      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8737      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8738      * @type Number
8739      */
8740     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8741     
8742     /**
8743      * The minimum size of the resizing element. (Defaults to 0)
8744      * @type Number
8745      */
8746     this.minSize = 0;
8747     
8748     /**
8749      * The maximum size of the resizing element. (Defaults to 2000)
8750      * @type Number
8751      */
8752     this.maxSize = 2000;
8753     
8754     /**
8755      * Whether to animate the transition to the new size
8756      * @type Boolean
8757      */
8758     this.animate = false;
8759     
8760     /**
8761      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8762      * @type Boolean
8763      */
8764     this.useShim = false;
8765     
8766     /** @private */
8767     this.shim = null;
8768     
8769     if(!existingProxy){
8770         /** @private */
8771         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8772     }else{
8773         this.proxy = Roo.get(existingProxy).dom;
8774     }
8775     /** @private */
8776     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8777     
8778     /** @private */
8779     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8780     
8781     /** @private */
8782     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8783     
8784     /** @private */
8785     this.dragSpecs = {};
8786     
8787     /**
8788      * @private The adapter to use to positon and resize elements
8789      */
8790     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8791     this.adapter.init(this);
8792     
8793     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8794         /** @private */
8795         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8796         this.el.addClass("x-splitbar-h");
8797     }else{
8798         /** @private */
8799         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8800         this.el.addClass("x-splitbar-v");
8801     }
8802     
8803     this.addEvents({
8804         /**
8805          * @event resize
8806          * Fires when the splitter is moved (alias for {@link #event-moved})
8807          * @param {Roo.SplitBar} this
8808          * @param {Number} newSize the new width or height
8809          */
8810         "resize" : true,
8811         /**
8812          * @event moved
8813          * Fires when the splitter is moved
8814          * @param {Roo.SplitBar} this
8815          * @param {Number} newSize the new width or height
8816          */
8817         "moved" : true,
8818         /**
8819          * @event beforeresize
8820          * Fires before the splitter is dragged
8821          * @param {Roo.SplitBar} this
8822          */
8823         "beforeresize" : true,
8824
8825         "beforeapply" : true
8826     });
8827
8828     Roo.util.Observable.call(this);
8829 };
8830
8831 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8832     onStartProxyDrag : function(x, y){
8833         this.fireEvent("beforeresize", this);
8834         if(!this.overlay){
8835             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8836             o.unselectable();
8837             o.enableDisplayMode("block");
8838             // all splitbars share the same overlay
8839             Roo.SplitBar.prototype.overlay = o;
8840         }
8841         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8842         this.overlay.show();
8843         Roo.get(this.proxy).setDisplayed("block");
8844         var size = this.adapter.getElementSize(this);
8845         this.activeMinSize = this.getMinimumSize();;
8846         this.activeMaxSize = this.getMaximumSize();;
8847         var c1 = size - this.activeMinSize;
8848         var c2 = Math.max(this.activeMaxSize - size, 0);
8849         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8850             this.dd.resetConstraints();
8851             this.dd.setXConstraint(
8852                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8853                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8854             );
8855             this.dd.setYConstraint(0, 0);
8856         }else{
8857             this.dd.resetConstraints();
8858             this.dd.setXConstraint(0, 0);
8859             this.dd.setYConstraint(
8860                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8861                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8862             );
8863          }
8864         this.dragSpecs.startSize = size;
8865         this.dragSpecs.startPoint = [x, y];
8866         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8867     },
8868     
8869     /** 
8870      * @private Called after the drag operation by the DDProxy
8871      */
8872     onEndProxyDrag : function(e){
8873         Roo.get(this.proxy).setDisplayed(false);
8874         var endPoint = Roo.lib.Event.getXY(e);
8875         if(this.overlay){
8876             this.overlay.hide();
8877         }
8878         var newSize;
8879         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8880             newSize = this.dragSpecs.startSize + 
8881                 (this.placement == Roo.SplitBar.LEFT ?
8882                     endPoint[0] - this.dragSpecs.startPoint[0] :
8883                     this.dragSpecs.startPoint[0] - endPoint[0]
8884                 );
8885         }else{
8886             newSize = this.dragSpecs.startSize + 
8887                 (this.placement == Roo.SplitBar.TOP ?
8888                     endPoint[1] - this.dragSpecs.startPoint[1] :
8889                     this.dragSpecs.startPoint[1] - endPoint[1]
8890                 );
8891         }
8892         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8893         if(newSize != this.dragSpecs.startSize){
8894             if(this.fireEvent('beforeapply', this, newSize) !== false){
8895                 this.adapter.setElementSize(this, newSize);
8896                 this.fireEvent("moved", this, newSize);
8897                 this.fireEvent("resize", this, newSize);
8898             }
8899         }
8900     },
8901     
8902     /**
8903      * Get the adapter this SplitBar uses
8904      * @return The adapter object
8905      */
8906     getAdapter : function(){
8907         return this.adapter;
8908     },
8909     
8910     /**
8911      * Set the adapter this SplitBar uses
8912      * @param {Object} adapter A SplitBar adapter object
8913      */
8914     setAdapter : function(adapter){
8915         this.adapter = adapter;
8916         this.adapter.init(this);
8917     },
8918     
8919     /**
8920      * Gets the minimum size for the resizing element
8921      * @return {Number} The minimum size
8922      */
8923     getMinimumSize : function(){
8924         return this.minSize;
8925     },
8926     
8927     /**
8928      * Sets the minimum size for the resizing element
8929      * @param {Number} minSize The minimum size
8930      */
8931     setMinimumSize : function(minSize){
8932         this.minSize = minSize;
8933     },
8934     
8935     /**
8936      * Gets the maximum size for the resizing element
8937      * @return {Number} The maximum size
8938      */
8939     getMaximumSize : function(){
8940         return this.maxSize;
8941     },
8942     
8943     /**
8944      * Sets the maximum size for the resizing element
8945      * @param {Number} maxSize The maximum size
8946      */
8947     setMaximumSize : function(maxSize){
8948         this.maxSize = maxSize;
8949     },
8950     
8951     /**
8952      * Sets the initialize size for the resizing element
8953      * @param {Number} size The initial size
8954      */
8955     setCurrentSize : function(size){
8956         var oldAnimate = this.animate;
8957         this.animate = false;
8958         this.adapter.setElementSize(this, size);
8959         this.animate = oldAnimate;
8960     },
8961     
8962     /**
8963      * Destroy this splitbar. 
8964      * @param {Boolean} removeEl True to remove the element
8965      */
8966     destroy : function(removeEl){
8967         if(this.shim){
8968             this.shim.remove();
8969         }
8970         this.dd.unreg();
8971         this.proxy.parentNode.removeChild(this.proxy);
8972         if(removeEl){
8973             this.el.remove();
8974         }
8975     }
8976 });
8977
8978 /**
8979  * @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.
8980  */
8981 Roo.SplitBar.createProxy = function(dir){
8982     var proxy = new Roo.Element(document.createElement("div"));
8983     proxy.unselectable();
8984     var cls = 'x-splitbar-proxy';
8985     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8986     document.body.appendChild(proxy.dom);
8987     return proxy.dom;
8988 };
8989
8990 /** 
8991  * @class Roo.SplitBar.BasicLayoutAdapter
8992  * Default Adapter. It assumes the splitter and resizing element are not positioned
8993  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8994  */
8995 Roo.SplitBar.BasicLayoutAdapter = function(){
8996 };
8997
8998 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8999     // do nothing for now
9000     init : function(s){
9001     
9002     },
9003     /**
9004      * Called before drag operations to get the current size of the resizing element. 
9005      * @param {Roo.SplitBar} s The SplitBar using this adapter
9006      */
9007      getElementSize : function(s){
9008         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9009             return s.resizingEl.getWidth();
9010         }else{
9011             return s.resizingEl.getHeight();
9012         }
9013     },
9014     
9015     /**
9016      * Called after drag operations to set the size of the resizing element.
9017      * @param {Roo.SplitBar} s The SplitBar using this adapter
9018      * @param {Number} newSize The new size to set
9019      * @param {Function} onComplete A function to be invoked when resizing is complete
9020      */
9021     setElementSize : function(s, newSize, onComplete){
9022         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9023             if(!s.animate){
9024                 s.resizingEl.setWidth(newSize);
9025                 if(onComplete){
9026                     onComplete(s, newSize);
9027                 }
9028             }else{
9029                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9030             }
9031         }else{
9032             
9033             if(!s.animate){
9034                 s.resizingEl.setHeight(newSize);
9035                 if(onComplete){
9036                     onComplete(s, newSize);
9037                 }
9038             }else{
9039                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9040             }
9041         }
9042     }
9043 };
9044
9045 /** 
9046  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9047  * @extends Roo.SplitBar.BasicLayoutAdapter
9048  * Adapter that  moves the splitter element to align with the resized sizing element. 
9049  * Used with an absolute positioned SplitBar.
9050  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9051  * document.body, make sure you assign an id to the body element.
9052  */
9053 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9054     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9055     this.container = Roo.get(container);
9056 };
9057
9058 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9059     init : function(s){
9060         this.basic.init(s);
9061     },
9062     
9063     getElementSize : function(s){
9064         return this.basic.getElementSize(s);
9065     },
9066     
9067     setElementSize : function(s, newSize, onComplete){
9068         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9069     },
9070     
9071     moveSplitter : function(s){
9072         var yes = Roo.SplitBar;
9073         switch(s.placement){
9074             case yes.LEFT:
9075                 s.el.setX(s.resizingEl.getRight());
9076                 break;
9077             case yes.RIGHT:
9078                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9079                 break;
9080             case yes.TOP:
9081                 s.el.setY(s.resizingEl.getBottom());
9082                 break;
9083             case yes.BOTTOM:
9084                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9085                 break;
9086         }
9087     }
9088 };
9089
9090 /**
9091  * Orientation constant - Create a vertical SplitBar
9092  * @static
9093  * @type Number
9094  */
9095 Roo.SplitBar.VERTICAL = 1;
9096
9097 /**
9098  * Orientation constant - Create a horizontal SplitBar
9099  * @static
9100  * @type Number
9101  */
9102 Roo.SplitBar.HORIZONTAL = 2;
9103
9104 /**
9105  * Placement constant - The resizing element is to the left of the splitter element
9106  * @static
9107  * @type Number
9108  */
9109 Roo.SplitBar.LEFT = 1;
9110
9111 /**
9112  * Placement constant - The resizing element is to the right of the splitter element
9113  * @static
9114  * @type Number
9115  */
9116 Roo.SplitBar.RIGHT = 2;
9117
9118 /**
9119  * Placement constant - The resizing element is positioned above the splitter element
9120  * @static
9121  * @type Number
9122  */
9123 Roo.SplitBar.TOP = 3;
9124
9125 /**
9126  * Placement constant - The resizing element is positioned under splitter element
9127  * @static
9128  * @type Number
9129  */
9130 Roo.SplitBar.BOTTOM = 4;
9131 /*
9132  * Based on:
9133  * Ext JS Library 1.1.1
9134  * Copyright(c) 2006-2007, Ext JS, LLC.
9135  *
9136  * Originally Released Under LGPL - original licence link has changed is not relivant.
9137  *
9138  * Fork - LGPL
9139  * <script type="text/javascript">
9140  */
9141
9142 /**
9143  * @class Roo.View
9144  * @extends Roo.util.Observable
9145  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9146  * This class also supports single and multi selection modes. <br>
9147  * Create a data model bound view:
9148  <pre><code>
9149  var store = new Roo.data.Store(...);
9150
9151  var view = new Roo.View({
9152     el : "my-element",
9153     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9154  
9155     singleSelect: true,
9156     selectedClass: "ydataview-selected",
9157     store: store
9158  });
9159
9160  // listen for node click?
9161  view.on("click", function(vw, index, node, e){
9162  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9163  });
9164
9165  // load XML data
9166  dataModel.load("foobar.xml");
9167  </code></pre>
9168  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9169  * <br><br>
9170  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9171  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9172  * 
9173  * Note: old style constructor is still suported (container, template, config)
9174  * 
9175  * @constructor
9176  * Create a new View
9177  * @param {Object} config The config object
9178  * 
9179  */
9180 Roo.View = function(config, depreciated_tpl, depreciated_config){
9181     
9182     if (typeof(depreciated_tpl) == 'undefined') {
9183         // new way.. - universal constructor.
9184         Roo.apply(this, config);
9185         this.el  = Roo.get(this.el);
9186     } else {
9187         // old format..
9188         this.el  = Roo.get(config);
9189         this.tpl = depreciated_tpl;
9190         Roo.apply(this, depreciated_config);
9191     }
9192     this.wrapEl  = this.el.wrap().wrap();
9193     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9194     
9195     
9196     if(typeof(this.tpl) == "string"){
9197         this.tpl = new Roo.Template(this.tpl);
9198     } else {
9199         // support xtype ctors..
9200         this.tpl = new Roo.factory(this.tpl, Roo);
9201     }
9202     
9203     
9204     this.tpl.compile();
9205    
9206   
9207     
9208      
9209     /** @private */
9210     this.addEvents({
9211         /**
9212          * @event beforeclick
9213          * Fires before a click is processed. Returns false to cancel the default action.
9214          * @param {Roo.View} this
9215          * @param {Number} index The index of the target node
9216          * @param {HTMLElement} node The target node
9217          * @param {Roo.EventObject} e The raw event object
9218          */
9219             "beforeclick" : true,
9220         /**
9221          * @event click
9222          * Fires when a template node is clicked.
9223          * @param {Roo.View} this
9224          * @param {Number} index The index of the target node
9225          * @param {HTMLElement} node The target node
9226          * @param {Roo.EventObject} e The raw event object
9227          */
9228             "click" : true,
9229         /**
9230          * @event dblclick
9231          * Fires when a template node is double clicked.
9232          * @param {Roo.View} this
9233          * @param {Number} index The index of the target node
9234          * @param {HTMLElement} node The target node
9235          * @param {Roo.EventObject} e The raw event object
9236          */
9237             "dblclick" : true,
9238         /**
9239          * @event contextmenu
9240          * Fires when a template node is right clicked.
9241          * @param {Roo.View} this
9242          * @param {Number} index The index of the target node
9243          * @param {HTMLElement} node The target node
9244          * @param {Roo.EventObject} e The raw event object
9245          */
9246             "contextmenu" : true,
9247         /**
9248          * @event selectionchange
9249          * Fires when the selected nodes change.
9250          * @param {Roo.View} this
9251          * @param {Array} selections Array of the selected nodes
9252          */
9253             "selectionchange" : true,
9254     
9255         /**
9256          * @event beforeselect
9257          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9258          * @param {Roo.View} this
9259          * @param {HTMLElement} node The node to be selected
9260          * @param {Array} selections Array of currently selected nodes
9261          */
9262             "beforeselect" : true,
9263         /**
9264          * @event preparedata
9265          * Fires on every row to render, to allow you to change the data.
9266          * @param {Roo.View} this
9267          * @param {Object} data to be rendered (change this)
9268          */
9269           "preparedata" : true
9270           
9271           
9272         });
9273
9274
9275
9276     this.el.on({
9277         "click": this.onClick,
9278         "dblclick": this.onDblClick,
9279         "contextmenu": this.onContextMenu,
9280         scope:this
9281     });
9282
9283     this.selections = [];
9284     this.nodes = [];
9285     this.cmp = new Roo.CompositeElementLite([]);
9286     if(this.store){
9287         this.store = Roo.factory(this.store, Roo.data);
9288         this.setStore(this.store, true);
9289     }
9290     
9291     if ( this.footer && this.footer.xtype) {
9292            
9293          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9294         
9295         this.footer.dataSource = this.store
9296         this.footer.container = fctr;
9297         this.footer = Roo.factory(this.footer, Roo);
9298         fctr.insertFirst(this.el);
9299         
9300         // this is a bit insane - as the paging toolbar seems to detach the el..
9301 //        dom.parentNode.parentNode.parentNode
9302          // they get detached?
9303     }
9304     
9305     
9306     Roo.View.superclass.constructor.call(this);
9307     
9308     
9309 };
9310
9311 Roo.extend(Roo.View, Roo.util.Observable, {
9312     
9313      /**
9314      * @cfg {Roo.data.Store} store Data store to load data from.
9315      */
9316     store : false,
9317     
9318     /**
9319      * @cfg {String|Roo.Element} el The container element.
9320      */
9321     el : '',
9322     
9323     /**
9324      * @cfg {String|Roo.Template} tpl The template used by this View 
9325      */
9326     tpl : false,
9327     /**
9328      * @cfg {String} dataName the named area of the template to use as the data area
9329      *                          Works with domtemplates roo-name="name"
9330      */
9331     dataName: false,
9332     /**
9333      * @cfg {String} selectedClass The css class to add to selected nodes
9334      */
9335     selectedClass : "x-view-selected",
9336      /**
9337      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9338      */
9339     emptyText : "",
9340     
9341     /**
9342      * @cfg {String} text to display on mask (default Loading)
9343      */
9344     mask : false,
9345     /**
9346      * @cfg {Boolean} multiSelect Allow multiple selection
9347      */
9348     multiSelect : false,
9349     /**
9350      * @cfg {Boolean} singleSelect Allow single selection
9351      */
9352     singleSelect:  false,
9353     
9354     /**
9355      * @cfg {Boolean} toggleSelect - selecting 
9356      */
9357     toggleSelect : false,
9358     
9359     /**
9360      * Returns the element this view is bound to.
9361      * @return {Roo.Element}
9362      */
9363     getEl : function(){
9364         return this.wrapEl;
9365     },
9366     
9367     
9368
9369     /**
9370      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9371      */
9372     refresh : function(){
9373         var t = this.tpl;
9374         
9375         // if we are using something like 'domtemplate', then
9376         // the what gets used is:
9377         // t.applySubtemplate(NAME, data, wrapping data..)
9378         // the outer template then get' applied with
9379         //     the store 'extra data'
9380         // and the body get's added to the
9381         //      roo-name="data" node?
9382         //      <span class='roo-tpl-{name}'></span> ?????
9383         
9384         
9385         
9386         this.clearSelections();
9387         this.el.update("");
9388         var html = [];
9389         var records = this.store.getRange();
9390         if(records.length < 1) {
9391             
9392             // is this valid??  = should it render a template??
9393             
9394             this.el.update(this.emptyText);
9395             return;
9396         }
9397         var el = this.el;
9398         if (this.dataName) {
9399             this.el.update(t.apply(this.store.meta)); //????
9400             el = this.el.child('.roo-tpl-' + this.dataName);
9401         }
9402         
9403         for(var i = 0, len = records.length; i < len; i++){
9404             var data = this.prepareData(records[i].data, i, records[i]);
9405             this.fireEvent("preparedata", this, data, i, records[i]);
9406             html[html.length] = Roo.util.Format.trim(
9407                 this.dataName ?
9408                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9409                     t.apply(data)
9410             );
9411         }
9412         
9413         
9414         
9415         el.update(html.join(""));
9416         this.nodes = el.dom.childNodes;
9417         this.updateIndexes(0);
9418     },
9419
9420     /**
9421      * Function to override to reformat the data that is sent to
9422      * the template for each node.
9423      * DEPRICATED - use the preparedata event handler.
9424      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9425      * a JSON object for an UpdateManager bound view).
9426      */
9427     prepareData : function(data, index, record)
9428     {
9429         this.fireEvent("preparedata", this, data, index, record);
9430         return data;
9431     },
9432
9433     onUpdate : function(ds, record){
9434         this.clearSelections();
9435         var index = this.store.indexOf(record);
9436         var n = this.nodes[index];
9437         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9438         n.parentNode.removeChild(n);
9439         this.updateIndexes(index, index);
9440     },
9441
9442     
9443     
9444 // --------- FIXME     
9445     onAdd : function(ds, records, index)
9446     {
9447         this.clearSelections();
9448         if(this.nodes.length == 0){
9449             this.refresh();
9450             return;
9451         }
9452         var n = this.nodes[index];
9453         for(var i = 0, len = records.length; i < len; i++){
9454             var d = this.prepareData(records[i].data, i, records[i]);
9455             if(n){
9456                 this.tpl.insertBefore(n, d);
9457             }else{
9458                 
9459                 this.tpl.append(this.el, d);
9460             }
9461         }
9462         this.updateIndexes(index);
9463     },
9464
9465     onRemove : function(ds, record, index){
9466         this.clearSelections();
9467         var el = this.dataName  ?
9468             this.el.child('.roo-tpl-' + this.dataName) :
9469             this.el; 
9470         el.dom.removeChild(this.nodes[index]);
9471         this.updateIndexes(index);
9472     },
9473
9474     /**
9475      * Refresh an individual node.
9476      * @param {Number} index
9477      */
9478     refreshNode : function(index){
9479         this.onUpdate(this.store, this.store.getAt(index));
9480     },
9481
9482     updateIndexes : function(startIndex, endIndex){
9483         var ns = this.nodes;
9484         startIndex = startIndex || 0;
9485         endIndex = endIndex || ns.length - 1;
9486         for(var i = startIndex; i <= endIndex; i++){
9487             ns[i].nodeIndex = i;
9488         }
9489     },
9490
9491     /**
9492      * Changes the data store this view uses and refresh the view.
9493      * @param {Store} store
9494      */
9495     setStore : function(store, initial){
9496         if(!initial && this.store){
9497             this.store.un("datachanged", this.refresh);
9498             this.store.un("add", this.onAdd);
9499             this.store.un("remove", this.onRemove);
9500             this.store.un("update", this.onUpdate);
9501             this.store.un("clear", this.refresh);
9502             this.store.un("beforeload", this.onBeforeLoad);
9503             this.store.un("load", this.onLoad);
9504             this.store.un("loadexception", this.onLoad);
9505         }
9506         if(store){
9507           
9508             store.on("datachanged", this.refresh, this);
9509             store.on("add", this.onAdd, this);
9510             store.on("remove", this.onRemove, this);
9511             store.on("update", this.onUpdate, this);
9512             store.on("clear", this.refresh, this);
9513             store.on("beforeload", this.onBeforeLoad, this);
9514             store.on("load", this.onLoad, this);
9515             store.on("loadexception", this.onLoad, this);
9516         }
9517         
9518         if(store){
9519             this.refresh();
9520         }
9521     },
9522     /**
9523      * onbeforeLoad - masks the loading area.
9524      *
9525      */
9526     onBeforeLoad : function()
9527     {
9528         this.el.update("");
9529         this.el.mask(this.mask ? this.mask : "Loading" ); 
9530     },
9531     onLoad : function ()
9532     {
9533         this.el.unmask();
9534     },
9535     
9536
9537     /**
9538      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9539      * @param {HTMLElement} node
9540      * @return {HTMLElement} The template node
9541      */
9542     findItemFromChild : function(node){
9543         var el = this.dataName  ?
9544             this.el.child('.roo-tpl-' + this.dataName,true) :
9545             this.el.dom; 
9546         
9547         if(!node || node.parentNode == el){
9548                     return node;
9549             }
9550             var p = node.parentNode;
9551             while(p && p != el){
9552             if(p.parentNode == el){
9553                 return p;
9554             }
9555             p = p.parentNode;
9556         }
9557             return null;
9558     },
9559
9560     /** @ignore */
9561     onClick : function(e){
9562         var item = this.findItemFromChild(e.getTarget());
9563         if(item){
9564             var index = this.indexOf(item);
9565             if(this.onItemClick(item, index, e) !== false){
9566                 this.fireEvent("click", this, index, item, e);
9567             }
9568         }else{
9569             this.clearSelections();
9570         }
9571     },
9572
9573     /** @ignore */
9574     onContextMenu : function(e){
9575         var item = this.findItemFromChild(e.getTarget());
9576         if(item){
9577             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9578         }
9579     },
9580
9581     /** @ignore */
9582     onDblClick : function(e){
9583         var item = this.findItemFromChild(e.getTarget());
9584         if(item){
9585             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9586         }
9587     },
9588
9589     onItemClick : function(item, index, e)
9590     {
9591         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9592             return false;
9593         }
9594         if (this.toggleSelect) {
9595             var m = this.isSelected(item) ? 'unselect' : 'select';
9596             Roo.log(m);
9597             var _t = this;
9598             _t[m](item, true, false);
9599             return true;
9600         }
9601         if(this.multiSelect || this.singleSelect){
9602             if(this.multiSelect && e.shiftKey && this.lastSelection){
9603                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9604             }else{
9605                 this.select(item, this.multiSelect && e.ctrlKey);
9606                 this.lastSelection = item;
9607             }
9608             e.preventDefault();
9609         }
9610         return true;
9611     },
9612
9613     /**
9614      * Get the number of selected nodes.
9615      * @return {Number}
9616      */
9617     getSelectionCount : function(){
9618         return this.selections.length;
9619     },
9620
9621     /**
9622      * Get the currently selected nodes.
9623      * @return {Array} An array of HTMLElements
9624      */
9625     getSelectedNodes : function(){
9626         return this.selections;
9627     },
9628
9629     /**
9630      * Get the indexes of the selected nodes.
9631      * @return {Array}
9632      */
9633     getSelectedIndexes : function(){
9634         var indexes = [], s = this.selections;
9635         for(var i = 0, len = s.length; i < len; i++){
9636             indexes.push(s[i].nodeIndex);
9637         }
9638         return indexes;
9639     },
9640
9641     /**
9642      * Clear all selections
9643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9644      */
9645     clearSelections : function(suppressEvent){
9646         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9647             this.cmp.elements = this.selections;
9648             this.cmp.removeClass(this.selectedClass);
9649             this.selections = [];
9650             if(!suppressEvent){
9651                 this.fireEvent("selectionchange", this, this.selections);
9652             }
9653         }
9654     },
9655
9656     /**
9657      * Returns true if the passed node is selected
9658      * @param {HTMLElement/Number} node The node or node index
9659      * @return {Boolean}
9660      */
9661     isSelected : function(node){
9662         var s = this.selections;
9663         if(s.length < 1){
9664             return false;
9665         }
9666         node = this.getNode(node);
9667         return s.indexOf(node) !== -1;
9668     },
9669
9670     /**
9671      * Selects nodes.
9672      * @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
9673      * @param {Boolean} keepExisting (optional) true to keep existing selections
9674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9675      */
9676     select : function(nodeInfo, keepExisting, suppressEvent){
9677         if(nodeInfo instanceof Array){
9678             if(!keepExisting){
9679                 this.clearSelections(true);
9680             }
9681             for(var i = 0, len = nodeInfo.length; i < len; i++){
9682                 this.select(nodeInfo[i], true, true);
9683             }
9684             return;
9685         } 
9686         var node = this.getNode(nodeInfo);
9687         if(!node || this.isSelected(node)){
9688             return; // already selected.
9689         }
9690         if(!keepExisting){
9691             this.clearSelections(true);
9692         }
9693         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9694             Roo.fly(node).addClass(this.selectedClass);
9695             this.selections.push(node);
9696             if(!suppressEvent){
9697                 this.fireEvent("selectionchange", this, this.selections);
9698             }
9699         }
9700         
9701         
9702     },
9703       /**
9704      * Unselects nodes.
9705      * @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
9706      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9707      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9708      */
9709     unselect : function(nodeInfo, keepExisting, suppressEvent)
9710     {
9711         if(nodeInfo instanceof Array){
9712             Roo.each(this.selections, function(s) {
9713                 this.unselect(s, nodeInfo);
9714             }, this);
9715             return;
9716         }
9717         var node = this.getNode(nodeInfo);
9718         if(!node || !this.isSelected(node)){
9719             Roo.log("not selected");
9720             return; // not selected.
9721         }
9722         // fireevent???
9723         var ns = [];
9724         Roo.each(this.selections, function(s) {
9725             if (s == node ) {
9726                 Roo.fly(node).removeClass(this.selectedClass);
9727
9728                 return;
9729             }
9730             ns.push(s);
9731         },this);
9732         
9733         this.selections= ns;
9734         this.fireEvent("selectionchange", this, this.selections);
9735     },
9736
9737     /**
9738      * Gets a template node.
9739      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9740      * @return {HTMLElement} The node or null if it wasn't found
9741      */
9742     getNode : function(nodeInfo){
9743         if(typeof nodeInfo == "string"){
9744             return document.getElementById(nodeInfo);
9745         }else if(typeof nodeInfo == "number"){
9746             return this.nodes[nodeInfo];
9747         }
9748         return nodeInfo;
9749     },
9750
9751     /**
9752      * Gets a range template nodes.
9753      * @param {Number} startIndex
9754      * @param {Number} endIndex
9755      * @return {Array} An array of nodes
9756      */
9757     getNodes : function(start, end){
9758         var ns = this.nodes;
9759         start = start || 0;
9760         end = typeof end == "undefined" ? ns.length - 1 : end;
9761         var nodes = [];
9762         if(start <= end){
9763             for(var i = start; i <= end; i++){
9764                 nodes.push(ns[i]);
9765             }
9766         } else{
9767             for(var i = start; i >= end; i--){
9768                 nodes.push(ns[i]);
9769             }
9770         }
9771         return nodes;
9772     },
9773
9774     /**
9775      * Finds the index of the passed node
9776      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9777      * @return {Number} The index of the node or -1
9778      */
9779     indexOf : function(node){
9780         node = this.getNode(node);
9781         if(typeof node.nodeIndex == "number"){
9782             return node.nodeIndex;
9783         }
9784         var ns = this.nodes;
9785         for(var i = 0, len = ns.length; i < len; i++){
9786             if(ns[i] == node){
9787                 return i;
9788             }
9789         }
9790         return -1;
9791     }
9792 });
9793 /*
9794  * Based on:
9795  * Ext JS Library 1.1.1
9796  * Copyright(c) 2006-2007, Ext JS, LLC.
9797  *
9798  * Originally Released Under LGPL - original licence link has changed is not relivant.
9799  *
9800  * Fork - LGPL
9801  * <script type="text/javascript">
9802  */
9803
9804 /**
9805  * @class Roo.JsonView
9806  * @extends Roo.View
9807  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9808 <pre><code>
9809 var view = new Roo.JsonView({
9810     container: "my-element",
9811     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9812     multiSelect: true, 
9813     jsonRoot: "data" 
9814 });
9815
9816 // listen for node click?
9817 view.on("click", function(vw, index, node, e){
9818     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9819 });
9820
9821 // direct load of JSON data
9822 view.load("foobar.php");
9823
9824 // Example from my blog list
9825 var tpl = new Roo.Template(
9826     '&lt;div class="entry"&gt;' +
9827     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9828     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9829     "&lt;/div&gt;&lt;hr /&gt;"
9830 );
9831
9832 var moreView = new Roo.JsonView({
9833     container :  "entry-list", 
9834     template : tpl,
9835     jsonRoot: "posts"
9836 });
9837 moreView.on("beforerender", this.sortEntries, this);
9838 moreView.load({
9839     url: "/blog/get-posts.php",
9840     params: "allposts=true",
9841     text: "Loading Blog Entries..."
9842 });
9843 </code></pre>
9844
9845 * Note: old code is supported with arguments : (container, template, config)
9846
9847
9848  * @constructor
9849  * Create a new JsonView
9850  * 
9851  * @param {Object} config The config object
9852  * 
9853  */
9854 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9855     
9856     
9857     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9858
9859     var um = this.el.getUpdateManager();
9860     um.setRenderer(this);
9861     um.on("update", this.onLoad, this);
9862     um.on("failure", this.onLoadException, this);
9863
9864     /**
9865      * @event beforerender
9866      * Fires before rendering of the downloaded JSON data.
9867      * @param {Roo.JsonView} this
9868      * @param {Object} data The JSON data loaded
9869      */
9870     /**
9871      * @event load
9872      * Fires when data is loaded.
9873      * @param {Roo.JsonView} this
9874      * @param {Object} data The JSON data loaded
9875      * @param {Object} response The raw Connect response object
9876      */
9877     /**
9878      * @event loadexception
9879      * Fires when loading fails.
9880      * @param {Roo.JsonView} this
9881      * @param {Object} response The raw Connect response object
9882      */
9883     this.addEvents({
9884         'beforerender' : true,
9885         'load' : true,
9886         'loadexception' : true
9887     });
9888 };
9889 Roo.extend(Roo.JsonView, Roo.View, {
9890     /**
9891      * @type {String} The root property in the loaded JSON object that contains the data
9892      */
9893     jsonRoot : "",
9894
9895     /**
9896      * Refreshes the view.
9897      */
9898     refresh : function(){
9899         this.clearSelections();
9900         this.el.update("");
9901         var html = [];
9902         var o = this.jsonData;
9903         if(o && o.length > 0){
9904             for(var i = 0, len = o.length; i < len; i++){
9905                 var data = this.prepareData(o[i], i, o);
9906                 html[html.length] = this.tpl.apply(data);
9907             }
9908         }else{
9909             html.push(this.emptyText);
9910         }
9911         this.el.update(html.join(""));
9912         this.nodes = this.el.dom.childNodes;
9913         this.updateIndexes(0);
9914     },
9915
9916     /**
9917      * 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.
9918      * @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:
9919      <pre><code>
9920      view.load({
9921          url: "your-url.php",
9922          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9923          callback: yourFunction,
9924          scope: yourObject, //(optional scope)
9925          discardUrl: false,
9926          nocache: false,
9927          text: "Loading...",
9928          timeout: 30,
9929          scripts: false
9930      });
9931      </code></pre>
9932      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9933      * 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.
9934      * @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}
9935      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9936      * @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.
9937      */
9938     load : function(){
9939         var um = this.el.getUpdateManager();
9940         um.update.apply(um, arguments);
9941     },
9942
9943     render : function(el, response){
9944         this.clearSelections();
9945         this.el.update("");
9946         var o;
9947         try{
9948             o = Roo.util.JSON.decode(response.responseText);
9949             if(this.jsonRoot){
9950                 
9951                 o = o[this.jsonRoot];
9952             }
9953         } catch(e){
9954         }
9955         /**
9956          * The current JSON data or null
9957          */
9958         this.jsonData = o;
9959         this.beforeRender();
9960         this.refresh();
9961     },
9962
9963 /**
9964  * Get the number of records in the current JSON dataset
9965  * @return {Number}
9966  */
9967     getCount : function(){
9968         return this.jsonData ? this.jsonData.length : 0;
9969     },
9970
9971 /**
9972  * Returns the JSON object for the specified node(s)
9973  * @param {HTMLElement/Array} node The node or an array of nodes
9974  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9975  * you get the JSON object for the node
9976  */
9977     getNodeData : function(node){
9978         if(node instanceof Array){
9979             var data = [];
9980             for(var i = 0, len = node.length; i < len; i++){
9981                 data.push(this.getNodeData(node[i]));
9982             }
9983             return data;
9984         }
9985         return this.jsonData[this.indexOf(node)] || null;
9986     },
9987
9988     beforeRender : function(){
9989         this.snapshot = this.jsonData;
9990         if(this.sortInfo){
9991             this.sort.apply(this, this.sortInfo);
9992         }
9993         this.fireEvent("beforerender", this, this.jsonData);
9994     },
9995
9996     onLoad : function(el, o){
9997         this.fireEvent("load", this, this.jsonData, o);
9998     },
9999
10000     onLoadException : function(el, o){
10001         this.fireEvent("loadexception", this, o);
10002     },
10003
10004 /**
10005  * Filter the data by a specific property.
10006  * @param {String} property A property on your JSON objects
10007  * @param {String/RegExp} value Either string that the property values
10008  * should start with, or a RegExp to test against the property
10009  */
10010     filter : function(property, value){
10011         if(this.jsonData){
10012             var data = [];
10013             var ss = this.snapshot;
10014             if(typeof value == "string"){
10015                 var vlen = value.length;
10016                 if(vlen == 0){
10017                     this.clearFilter();
10018                     return;
10019                 }
10020                 value = value.toLowerCase();
10021                 for(var i = 0, len = ss.length; i < len; i++){
10022                     var o = ss[i];
10023                     if(o[property].substr(0, vlen).toLowerCase() == value){
10024                         data.push(o);
10025                     }
10026                 }
10027             } else if(value.exec){ // regex?
10028                 for(var i = 0, len = ss.length; i < len; i++){
10029                     var o = ss[i];
10030                     if(value.test(o[property])){
10031                         data.push(o);
10032                     }
10033                 }
10034             } else{
10035                 return;
10036             }
10037             this.jsonData = data;
10038             this.refresh();
10039         }
10040     },
10041
10042 /**
10043  * Filter by a function. The passed function will be called with each
10044  * object in the current dataset. If the function returns true the value is kept,
10045  * otherwise it is filtered.
10046  * @param {Function} fn
10047  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10048  */
10049     filterBy : function(fn, scope){
10050         if(this.jsonData){
10051             var data = [];
10052             var ss = this.snapshot;
10053             for(var i = 0, len = ss.length; i < len; i++){
10054                 var o = ss[i];
10055                 if(fn.call(scope || this, o)){
10056                     data.push(o);
10057                 }
10058             }
10059             this.jsonData = data;
10060             this.refresh();
10061         }
10062     },
10063
10064 /**
10065  * Clears the current filter.
10066  */
10067     clearFilter : function(){
10068         if(this.snapshot && this.jsonData != this.snapshot){
10069             this.jsonData = this.snapshot;
10070             this.refresh();
10071         }
10072     },
10073
10074
10075 /**
10076  * Sorts the data for this view and refreshes it.
10077  * @param {String} property A property on your JSON objects to sort on
10078  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10079  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10080  */
10081     sort : function(property, dir, sortType){
10082         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10083         if(this.jsonData){
10084             var p = property;
10085             var dsc = dir && dir.toLowerCase() == "desc";
10086             var f = function(o1, o2){
10087                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10088                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10089                 ;
10090                 if(v1 < v2){
10091                     return dsc ? +1 : -1;
10092                 } else if(v1 > v2){
10093                     return dsc ? -1 : +1;
10094                 } else{
10095                     return 0;
10096                 }
10097             };
10098             this.jsonData.sort(f);
10099             this.refresh();
10100             if(this.jsonData != this.snapshot){
10101                 this.snapshot.sort(f);
10102             }
10103         }
10104     }
10105 });/*
10106  * Based on:
10107  * Ext JS Library 1.1.1
10108  * Copyright(c) 2006-2007, Ext JS, LLC.
10109  *
10110  * Originally Released Under LGPL - original licence link has changed is not relivant.
10111  *
10112  * Fork - LGPL
10113  * <script type="text/javascript">
10114  */
10115  
10116
10117 /**
10118  * @class Roo.ColorPalette
10119  * @extends Roo.Component
10120  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10121  * Here's an example of typical usage:
10122  * <pre><code>
10123 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10124 cp.render('my-div');
10125
10126 cp.on('select', function(palette, selColor){
10127     // do something with selColor
10128 });
10129 </code></pre>
10130  * @constructor
10131  * Create a new ColorPalette
10132  * @param {Object} config The config object
10133  */
10134 Roo.ColorPalette = function(config){
10135     Roo.ColorPalette.superclass.constructor.call(this, config);
10136     this.addEvents({
10137         /**
10138              * @event select
10139              * Fires when a color is selected
10140              * @param {ColorPalette} this
10141              * @param {String} color The 6-digit color hex code (without the # symbol)
10142              */
10143         select: true
10144     });
10145
10146     if(this.handler){
10147         this.on("select", this.handler, this.scope, true);
10148     }
10149 };
10150 Roo.extend(Roo.ColorPalette, Roo.Component, {
10151     /**
10152      * @cfg {String} itemCls
10153      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10154      */
10155     itemCls : "x-color-palette",
10156     /**
10157      * @cfg {String} value
10158      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10159      * the hex codes are case-sensitive.
10160      */
10161     value : null,
10162     clickEvent:'click',
10163     // private
10164     ctype: "Roo.ColorPalette",
10165
10166     /**
10167      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10168      */
10169     allowReselect : false,
10170
10171     /**
10172      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10173      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10174      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10175      * of colors with the width setting until the box is symmetrical.</p>
10176      * <p>You can override individual colors if needed:</p>
10177      * <pre><code>
10178 var cp = new Roo.ColorPalette();
10179 cp.colors[0] = "FF0000";  // change the first box to red
10180 </code></pre>
10181
10182 Or you can provide a custom array of your own for complete control:
10183 <pre><code>
10184 var cp = new Roo.ColorPalette();
10185 cp.colors = ["000000", "993300", "333300"];
10186 </code></pre>
10187      * @type Array
10188      */
10189     colors : [
10190         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10191         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10192         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10193         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10194         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10195     ],
10196
10197     // private
10198     onRender : function(container, position){
10199         var t = new Roo.MasterTemplate(
10200             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10201         );
10202         var c = this.colors;
10203         for(var i = 0, len = c.length; i < len; i++){
10204             t.add([c[i]]);
10205         }
10206         var el = document.createElement("div");
10207         el.className = this.itemCls;
10208         t.overwrite(el);
10209         container.dom.insertBefore(el, position);
10210         this.el = Roo.get(el);
10211         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10212         if(this.clickEvent != 'click'){
10213             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10214         }
10215     },
10216
10217     // private
10218     afterRender : function(){
10219         Roo.ColorPalette.superclass.afterRender.call(this);
10220         if(this.value){
10221             var s = this.value;
10222             this.value = null;
10223             this.select(s);
10224         }
10225     },
10226
10227     // private
10228     handleClick : function(e, t){
10229         e.preventDefault();
10230         if(!this.disabled){
10231             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10232             this.select(c.toUpperCase());
10233         }
10234     },
10235
10236     /**
10237      * Selects the specified color in the palette (fires the select event)
10238      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10239      */
10240     select : function(color){
10241         color = color.replace("#", "");
10242         if(color != this.value || this.allowReselect){
10243             var el = this.el;
10244             if(this.value){
10245                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10246             }
10247             el.child("a.color-"+color).addClass("x-color-palette-sel");
10248             this.value = color;
10249             this.fireEvent("select", this, color);
10250         }
10251     }
10252 });/*
10253  * Based on:
10254  * Ext JS Library 1.1.1
10255  * Copyright(c) 2006-2007, Ext JS, LLC.
10256  *
10257  * Originally Released Under LGPL - original licence link has changed is not relivant.
10258  *
10259  * Fork - LGPL
10260  * <script type="text/javascript">
10261  */
10262  
10263 /**
10264  * @class Roo.DatePicker
10265  * @extends Roo.Component
10266  * Simple date picker class.
10267  * @constructor
10268  * Create a new DatePicker
10269  * @param {Object} config The config object
10270  */
10271 Roo.DatePicker = function(config){
10272     Roo.DatePicker.superclass.constructor.call(this, config);
10273
10274     this.value = config && config.value ?
10275                  config.value.clearTime() : new Date().clearTime();
10276
10277     this.addEvents({
10278         /**
10279              * @event select
10280              * Fires when a date is selected
10281              * @param {DatePicker} this
10282              * @param {Date} date The selected date
10283              */
10284         'select': true,
10285         /**
10286              * @event monthchange
10287              * Fires when the displayed month changes 
10288              * @param {DatePicker} this
10289              * @param {Date} date The selected month
10290              */
10291         'monthchange': true
10292     });
10293
10294     if(this.handler){
10295         this.on("select", this.handler,  this.scope || this);
10296     }
10297     // build the disabledDatesRE
10298     if(!this.disabledDatesRE && this.disabledDates){
10299         var dd = this.disabledDates;
10300         var re = "(?:";
10301         for(var i = 0; i < dd.length; i++){
10302             re += dd[i];
10303             if(i != dd.length-1) re += "|";
10304         }
10305         this.disabledDatesRE = new RegExp(re + ")");
10306     }
10307 };
10308
10309 Roo.extend(Roo.DatePicker, Roo.Component, {
10310     /**
10311      * @cfg {String} todayText
10312      * The text to display on the button that selects the current date (defaults to "Today")
10313      */
10314     todayText : "Today",
10315     /**
10316      * @cfg {String} okText
10317      * The text to display on the ok button
10318      */
10319     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10320     /**
10321      * @cfg {String} cancelText
10322      * The text to display on the cancel button
10323      */
10324     cancelText : "Cancel",
10325     /**
10326      * @cfg {String} todayTip
10327      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10328      */
10329     todayTip : "{0} (Spacebar)",
10330     /**
10331      * @cfg {Date} minDate
10332      * Minimum allowable date (JavaScript date object, defaults to null)
10333      */
10334     minDate : null,
10335     /**
10336      * @cfg {Date} maxDate
10337      * Maximum allowable date (JavaScript date object, defaults to null)
10338      */
10339     maxDate : null,
10340     /**
10341      * @cfg {String} minText
10342      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10343      */
10344     minText : "This date is before the minimum date",
10345     /**
10346      * @cfg {String} maxText
10347      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10348      */
10349     maxText : "This date is after the maximum date",
10350     /**
10351      * @cfg {String} format
10352      * The default date format string which can be overriden for localization support.  The format must be
10353      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10354      */
10355     format : "m/d/y",
10356     /**
10357      * @cfg {Array} disabledDays
10358      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10359      */
10360     disabledDays : null,
10361     /**
10362      * @cfg {String} disabledDaysText
10363      * The tooltip to display when the date falls on a disabled day (defaults to "")
10364      */
10365     disabledDaysText : "",
10366     /**
10367      * @cfg {RegExp} disabledDatesRE
10368      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10369      */
10370     disabledDatesRE : null,
10371     /**
10372      * @cfg {String} disabledDatesText
10373      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10374      */
10375     disabledDatesText : "",
10376     /**
10377      * @cfg {Boolean} constrainToViewport
10378      * True to constrain the date picker to the viewport (defaults to true)
10379      */
10380     constrainToViewport : true,
10381     /**
10382      * @cfg {Array} monthNames
10383      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10384      */
10385     monthNames : Date.monthNames,
10386     /**
10387      * @cfg {Array} dayNames
10388      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10389      */
10390     dayNames : Date.dayNames,
10391     /**
10392      * @cfg {String} nextText
10393      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10394      */
10395     nextText: 'Next Month (Control+Right)',
10396     /**
10397      * @cfg {String} prevText
10398      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10399      */
10400     prevText: 'Previous Month (Control+Left)',
10401     /**
10402      * @cfg {String} monthYearText
10403      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10404      */
10405     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10406     /**
10407      * @cfg {Number} startDay
10408      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10409      */
10410     startDay : 0,
10411     /**
10412      * @cfg {Bool} showClear
10413      * Show a clear button (usefull for date form elements that can be blank.)
10414      */
10415     
10416     showClear: false,
10417     
10418     /**
10419      * Sets the value of the date field
10420      * @param {Date} value The date to set
10421      */
10422     setValue : function(value){
10423         var old = this.value;
10424         
10425         if (typeof(value) == 'string') {
10426          
10427             value = Date.parseDate(value, this.format);
10428         }
10429         if (!value) {
10430             value = new Date();
10431         }
10432         
10433         this.value = value.clearTime(true);
10434         if(this.el){
10435             this.update(this.value);
10436         }
10437     },
10438
10439     /**
10440      * Gets the current selected value of the date field
10441      * @return {Date} The selected date
10442      */
10443     getValue : function(){
10444         return this.value;
10445     },
10446
10447     // private
10448     focus : function(){
10449         if(this.el){
10450             this.update(this.activeDate);
10451         }
10452     },
10453
10454     // privateval
10455     onRender : function(container, position){
10456         
10457         var m = [
10458              '<table cellspacing="0">',
10459                 '<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>',
10460                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10461         var dn = this.dayNames;
10462         for(var i = 0; i < 7; i++){
10463             var d = this.startDay+i;
10464             if(d > 6){
10465                 d = d-7;
10466             }
10467             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10468         }
10469         m[m.length] = "</tr></thead><tbody><tr>";
10470         for(var i = 0; i < 42; i++) {
10471             if(i % 7 == 0 && i != 0){
10472                 m[m.length] = "</tr><tr>";
10473             }
10474             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10475         }
10476         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10477             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10478
10479         var el = document.createElement("div");
10480         el.className = "x-date-picker";
10481         el.innerHTML = m.join("");
10482
10483         container.dom.insertBefore(el, position);
10484
10485         this.el = Roo.get(el);
10486         this.eventEl = Roo.get(el.firstChild);
10487
10488         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10489             handler: this.showPrevMonth,
10490             scope: this,
10491             preventDefault:true,
10492             stopDefault:true
10493         });
10494
10495         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10496             handler: this.showNextMonth,
10497             scope: this,
10498             preventDefault:true,
10499             stopDefault:true
10500         });
10501
10502         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10503
10504         this.monthPicker = this.el.down('div.x-date-mp');
10505         this.monthPicker.enableDisplayMode('block');
10506         
10507         var kn = new Roo.KeyNav(this.eventEl, {
10508             "left" : function(e){
10509                 e.ctrlKey ?
10510                     this.showPrevMonth() :
10511                     this.update(this.activeDate.add("d", -1));
10512             },
10513
10514             "right" : function(e){
10515                 e.ctrlKey ?
10516                     this.showNextMonth() :
10517                     this.update(this.activeDate.add("d", 1));
10518             },
10519
10520             "up" : function(e){
10521                 e.ctrlKey ?
10522                     this.showNextYear() :
10523                     this.update(this.activeDate.add("d", -7));
10524             },
10525
10526             "down" : function(e){
10527                 e.ctrlKey ?
10528                     this.showPrevYear() :
10529                     this.update(this.activeDate.add("d", 7));
10530             },
10531
10532             "pageUp" : function(e){
10533                 this.showNextMonth();
10534             },
10535
10536             "pageDown" : function(e){
10537                 this.showPrevMonth();
10538             },
10539
10540             "enter" : function(e){
10541                 e.stopPropagation();
10542                 return true;
10543             },
10544
10545             scope : this
10546         });
10547
10548         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10549
10550         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10551
10552         this.el.unselectable();
10553         
10554         this.cells = this.el.select("table.x-date-inner tbody td");
10555         this.textNodes = this.el.query("table.x-date-inner tbody span");
10556
10557         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10558             text: "&#160;",
10559             tooltip: this.monthYearText
10560         });
10561
10562         this.mbtn.on('click', this.showMonthPicker, this);
10563         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10564
10565
10566         var today = (new Date()).dateFormat(this.format);
10567         
10568         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10569         if (this.showClear) {
10570             baseTb.add( new Roo.Toolbar.Fill());
10571         }
10572         baseTb.add({
10573             text: String.format(this.todayText, today),
10574             tooltip: String.format(this.todayTip, today),
10575             handler: this.selectToday,
10576             scope: this
10577         });
10578         
10579         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10580             
10581         //});
10582         if (this.showClear) {
10583             
10584             baseTb.add( new Roo.Toolbar.Fill());
10585             baseTb.add({
10586                 text: '&#160;',
10587                 cls: 'x-btn-icon x-btn-clear',
10588                 handler: function() {
10589                     //this.value = '';
10590                     this.fireEvent("select", this, '');
10591                 },
10592                 scope: this
10593             });
10594         }
10595         
10596         
10597         if(Roo.isIE){
10598             this.el.repaint();
10599         }
10600         this.update(this.value);
10601     },
10602
10603     createMonthPicker : function(){
10604         if(!this.monthPicker.dom.firstChild){
10605             var buf = ['<table border="0" cellspacing="0">'];
10606             for(var i = 0; i < 6; i++){
10607                 buf.push(
10608                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10609                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10610                     i == 0 ?
10611                     '<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>' :
10612                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10613                 );
10614             }
10615             buf.push(
10616                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10617                     this.okText,
10618                     '</button><button type="button" class="x-date-mp-cancel">',
10619                     this.cancelText,
10620                     '</button></td></tr>',
10621                 '</table>'
10622             );
10623             this.monthPicker.update(buf.join(''));
10624             this.monthPicker.on('click', this.onMonthClick, this);
10625             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10626
10627             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10628             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10629
10630             this.mpMonths.each(function(m, a, i){
10631                 i += 1;
10632                 if((i%2) == 0){
10633                     m.dom.xmonth = 5 + Math.round(i * .5);
10634                 }else{
10635                     m.dom.xmonth = Math.round((i-1) * .5);
10636                 }
10637             });
10638         }
10639     },
10640
10641     showMonthPicker : function(){
10642         this.createMonthPicker();
10643         var size = this.el.getSize();
10644         this.monthPicker.setSize(size);
10645         this.monthPicker.child('table').setSize(size);
10646
10647         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10648         this.updateMPMonth(this.mpSelMonth);
10649         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10650         this.updateMPYear(this.mpSelYear);
10651
10652         this.monthPicker.slideIn('t', {duration:.2});
10653     },
10654
10655     updateMPYear : function(y){
10656         this.mpyear = y;
10657         var ys = this.mpYears.elements;
10658         for(var i = 1; i <= 10; i++){
10659             var td = ys[i-1], y2;
10660             if((i%2) == 0){
10661                 y2 = y + Math.round(i * .5);
10662                 td.firstChild.innerHTML = y2;
10663                 td.xyear = y2;
10664             }else{
10665                 y2 = y - (5-Math.round(i * .5));
10666                 td.firstChild.innerHTML = y2;
10667                 td.xyear = y2;
10668             }
10669             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10670         }
10671     },
10672
10673     updateMPMonth : function(sm){
10674         this.mpMonths.each(function(m, a, i){
10675             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10676         });
10677     },
10678
10679     selectMPMonth: function(m){
10680         
10681     },
10682
10683     onMonthClick : function(e, t){
10684         e.stopEvent();
10685         var el = new Roo.Element(t), pn;
10686         if(el.is('button.x-date-mp-cancel')){
10687             this.hideMonthPicker();
10688         }
10689         else if(el.is('button.x-date-mp-ok')){
10690             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10691             this.hideMonthPicker();
10692         }
10693         else if(pn = el.up('td.x-date-mp-month', 2)){
10694             this.mpMonths.removeClass('x-date-mp-sel');
10695             pn.addClass('x-date-mp-sel');
10696             this.mpSelMonth = pn.dom.xmonth;
10697         }
10698         else if(pn = el.up('td.x-date-mp-year', 2)){
10699             this.mpYears.removeClass('x-date-mp-sel');
10700             pn.addClass('x-date-mp-sel');
10701             this.mpSelYear = pn.dom.xyear;
10702         }
10703         else if(el.is('a.x-date-mp-prev')){
10704             this.updateMPYear(this.mpyear-10);
10705         }
10706         else if(el.is('a.x-date-mp-next')){
10707             this.updateMPYear(this.mpyear+10);
10708         }
10709     },
10710
10711     onMonthDblClick : function(e, t){
10712         e.stopEvent();
10713         var el = new Roo.Element(t), pn;
10714         if(pn = el.up('td.x-date-mp-month', 2)){
10715             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10716             this.hideMonthPicker();
10717         }
10718         else if(pn = el.up('td.x-date-mp-year', 2)){
10719             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10720             this.hideMonthPicker();
10721         }
10722     },
10723
10724     hideMonthPicker : function(disableAnim){
10725         if(this.monthPicker){
10726             if(disableAnim === true){
10727                 this.monthPicker.hide();
10728             }else{
10729                 this.monthPicker.slideOut('t', {duration:.2});
10730             }
10731         }
10732     },
10733
10734     // private
10735     showPrevMonth : function(e){
10736         this.update(this.activeDate.add("mo", -1));
10737     },
10738
10739     // private
10740     showNextMonth : function(e){
10741         this.update(this.activeDate.add("mo", 1));
10742     },
10743
10744     // private
10745     showPrevYear : function(){
10746         this.update(this.activeDate.add("y", -1));
10747     },
10748
10749     // private
10750     showNextYear : function(){
10751         this.update(this.activeDate.add("y", 1));
10752     },
10753
10754     // private
10755     handleMouseWheel : function(e){
10756         var delta = e.getWheelDelta();
10757         if(delta > 0){
10758             this.showPrevMonth();
10759             e.stopEvent();
10760         } else if(delta < 0){
10761             this.showNextMonth();
10762             e.stopEvent();
10763         }
10764     },
10765
10766     // private
10767     handleDateClick : function(e, t){
10768         e.stopEvent();
10769         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10770             this.setValue(new Date(t.dateValue));
10771             this.fireEvent("select", this, this.value);
10772         }
10773     },
10774
10775     // private
10776     selectToday : function(){
10777         this.setValue(new Date().clearTime());
10778         this.fireEvent("select", this, this.value);
10779     },
10780
10781     // private
10782     update : function(date)
10783     {
10784         var vd = this.activeDate;
10785         this.activeDate = date;
10786         if(vd && this.el){
10787             var t = date.getTime();
10788             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10789                 this.cells.removeClass("x-date-selected");
10790                 this.cells.each(function(c){
10791                    if(c.dom.firstChild.dateValue == t){
10792                        c.addClass("x-date-selected");
10793                        setTimeout(function(){
10794                             try{c.dom.firstChild.focus();}catch(e){}
10795                        }, 50);
10796                        return false;
10797                    }
10798                 });
10799                 return;
10800             }
10801         }
10802         
10803         var days = date.getDaysInMonth();
10804         var firstOfMonth = date.getFirstDateOfMonth();
10805         var startingPos = firstOfMonth.getDay()-this.startDay;
10806
10807         if(startingPos <= this.startDay){
10808             startingPos += 7;
10809         }
10810
10811         var pm = date.add("mo", -1);
10812         var prevStart = pm.getDaysInMonth()-startingPos;
10813
10814         var cells = this.cells.elements;
10815         var textEls = this.textNodes;
10816         days += startingPos;
10817
10818         // convert everything to numbers so it's fast
10819         var day = 86400000;
10820         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10821         var today = new Date().clearTime().getTime();
10822         var sel = date.clearTime().getTime();
10823         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10824         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10825         var ddMatch = this.disabledDatesRE;
10826         var ddText = this.disabledDatesText;
10827         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10828         var ddaysText = this.disabledDaysText;
10829         var format = this.format;
10830
10831         var setCellClass = function(cal, cell){
10832             cell.title = "";
10833             var t = d.getTime();
10834             cell.firstChild.dateValue = t;
10835             if(t == today){
10836                 cell.className += " x-date-today";
10837                 cell.title = cal.todayText;
10838             }
10839             if(t == sel){
10840                 cell.className += " x-date-selected";
10841                 setTimeout(function(){
10842                     try{cell.firstChild.focus();}catch(e){}
10843                 }, 50);
10844             }
10845             // disabling
10846             if(t < min) {
10847                 cell.className = " x-date-disabled";
10848                 cell.title = cal.minText;
10849                 return;
10850             }
10851             if(t > max) {
10852                 cell.className = " x-date-disabled";
10853                 cell.title = cal.maxText;
10854                 return;
10855             }
10856             if(ddays){
10857                 if(ddays.indexOf(d.getDay()) != -1){
10858                     cell.title = ddaysText;
10859                     cell.className = " x-date-disabled";
10860                 }
10861             }
10862             if(ddMatch && format){
10863                 var fvalue = d.dateFormat(format);
10864                 if(ddMatch.test(fvalue)){
10865                     cell.title = ddText.replace("%0", fvalue);
10866                     cell.className = " x-date-disabled";
10867                 }
10868             }
10869         };
10870
10871         var i = 0;
10872         for(; i < startingPos; i++) {
10873             textEls[i].innerHTML = (++prevStart);
10874             d.setDate(d.getDate()+1);
10875             cells[i].className = "x-date-prevday";
10876             setCellClass(this, cells[i]);
10877         }
10878         for(; i < days; i++){
10879             intDay = i - startingPos + 1;
10880             textEls[i].innerHTML = (intDay);
10881             d.setDate(d.getDate()+1);
10882             cells[i].className = "x-date-active";
10883             setCellClass(this, cells[i]);
10884         }
10885         var extraDays = 0;
10886         for(; i < 42; i++) {
10887              textEls[i].innerHTML = (++extraDays);
10888              d.setDate(d.getDate()+1);
10889              cells[i].className = "x-date-nextday";
10890              setCellClass(this, cells[i]);
10891         }
10892
10893         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10894         this.fireEvent('monthchange', this, date);
10895         
10896         if(!this.internalRender){
10897             var main = this.el.dom.firstChild;
10898             var w = main.offsetWidth;
10899             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10900             Roo.fly(main).setWidth(w);
10901             this.internalRender = true;
10902             // opera does not respect the auto grow header center column
10903             // then, after it gets a width opera refuses to recalculate
10904             // without a second pass
10905             if(Roo.isOpera && !this.secondPass){
10906                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10907                 this.secondPass = true;
10908                 this.update.defer(10, this, [date]);
10909             }
10910         }
10911         
10912         
10913     }
10914 });        /*
10915  * Based on:
10916  * Ext JS Library 1.1.1
10917  * Copyright(c) 2006-2007, Ext JS, LLC.
10918  *
10919  * Originally Released Under LGPL - original licence link has changed is not relivant.
10920  *
10921  * Fork - LGPL
10922  * <script type="text/javascript">
10923  */
10924 /**
10925  * @class Roo.TabPanel
10926  * @extends Roo.util.Observable
10927  * A lightweight tab container.
10928  * <br><br>
10929  * Usage:
10930  * <pre><code>
10931 // basic tabs 1, built from existing content
10932 var tabs = new Roo.TabPanel("tabs1");
10933 tabs.addTab("script", "View Script");
10934 tabs.addTab("markup", "View Markup");
10935 tabs.activate("script");
10936
10937 // more advanced tabs, built from javascript
10938 var jtabs = new Roo.TabPanel("jtabs");
10939 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10940
10941 // set up the UpdateManager
10942 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10943 var updater = tab2.getUpdateManager();
10944 updater.setDefaultUrl("ajax1.htm");
10945 tab2.on('activate', updater.refresh, updater, true);
10946
10947 // Use setUrl for Ajax loading
10948 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10949 tab3.setUrl("ajax2.htm", null, true);
10950
10951 // Disabled tab
10952 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10953 tab4.disable();
10954
10955 jtabs.activate("jtabs-1");
10956  * </code></pre>
10957  * @constructor
10958  * Create a new TabPanel.
10959  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10960  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10961  */
10962 Roo.TabPanel = function(container, config){
10963     /**
10964     * The container element for this TabPanel.
10965     * @type Roo.Element
10966     */
10967     this.el = Roo.get(container, true);
10968     if(config){
10969         if(typeof config == "boolean"){
10970             this.tabPosition = config ? "bottom" : "top";
10971         }else{
10972             Roo.apply(this, config);
10973         }
10974     }
10975     if(this.tabPosition == "bottom"){
10976         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10977         this.el.addClass("x-tabs-bottom");
10978     }
10979     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10980     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10981     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10982     if(Roo.isIE){
10983         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10984     }
10985     if(this.tabPosition != "bottom"){
10986         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10987          * @type Roo.Element
10988          */
10989         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10990         this.el.addClass("x-tabs-top");
10991     }
10992     this.items = [];
10993
10994     this.bodyEl.setStyle("position", "relative");
10995
10996     this.active = null;
10997     this.activateDelegate = this.activate.createDelegate(this);
10998
10999     this.addEvents({
11000         /**
11001          * @event tabchange
11002          * Fires when the active tab changes
11003          * @param {Roo.TabPanel} this
11004          * @param {Roo.TabPanelItem} activePanel The new active tab
11005          */
11006         "tabchange": true,
11007         /**
11008          * @event beforetabchange
11009          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11010          * @param {Roo.TabPanel} this
11011          * @param {Object} e Set cancel to true on this object to cancel the tab change
11012          * @param {Roo.TabPanelItem} tab The tab being changed to
11013          */
11014         "beforetabchange" : true
11015     });
11016
11017     Roo.EventManager.onWindowResize(this.onResize, this);
11018     this.cpad = this.el.getPadding("lr");
11019     this.hiddenCount = 0;
11020
11021
11022     // toolbar on the tabbar support...
11023     if (this.toolbar) {
11024         var tcfg = this.toolbar;
11025         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11026         this.toolbar = new Roo.Toolbar(tcfg);
11027         if (Roo.isSafari) {
11028             var tbl = tcfg.container.child('table', true);
11029             tbl.setAttribute('width', '100%');
11030         }
11031         
11032     }
11033    
11034
11035
11036     Roo.TabPanel.superclass.constructor.call(this);
11037 };
11038
11039 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11040     /*
11041      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11042      */
11043     tabPosition : "top",
11044     /*
11045      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11046      */
11047     currentTabWidth : 0,
11048     /*
11049      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11050      */
11051     minTabWidth : 40,
11052     /*
11053      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11054      */
11055     maxTabWidth : 250,
11056     /*
11057      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11058      */
11059     preferredTabWidth : 175,
11060     /*
11061      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11062      */
11063     resizeTabs : false,
11064     /*
11065      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11066      */
11067     monitorResize : true,
11068     /*
11069      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11070      */
11071     toolbar : false,
11072
11073     /**
11074      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11075      * @param {String} id The id of the div to use <b>or create</b>
11076      * @param {String} text The text for the tab
11077      * @param {String} content (optional) Content to put in the TabPanelItem body
11078      * @param {Boolean} closable (optional) True to create a close icon on the tab
11079      * @return {Roo.TabPanelItem} The created TabPanelItem
11080      */
11081     addTab : function(id, text, content, closable){
11082         var item = new Roo.TabPanelItem(this, id, text, closable);
11083         this.addTabItem(item);
11084         if(content){
11085             item.setContent(content);
11086         }
11087         return item;
11088     },
11089
11090     /**
11091      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11092      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11093      * @return {Roo.TabPanelItem}
11094      */
11095     getTab : function(id){
11096         return this.items[id];
11097     },
11098
11099     /**
11100      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11101      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11102      */
11103     hideTab : function(id){
11104         var t = this.items[id];
11105         if(!t.isHidden()){
11106            t.setHidden(true);
11107            this.hiddenCount++;
11108            this.autoSizeTabs();
11109         }
11110     },
11111
11112     /**
11113      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11114      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11115      */
11116     unhideTab : function(id){
11117         var t = this.items[id];
11118         if(t.isHidden()){
11119            t.setHidden(false);
11120            this.hiddenCount--;
11121            this.autoSizeTabs();
11122         }
11123     },
11124
11125     /**
11126      * Adds an existing {@link Roo.TabPanelItem}.
11127      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11128      */
11129     addTabItem : function(item){
11130         this.items[item.id] = item;
11131         this.items.push(item);
11132         if(this.resizeTabs){
11133            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11134            this.autoSizeTabs();
11135         }else{
11136             item.autoSize();
11137         }
11138     },
11139
11140     /**
11141      * Removes a {@link Roo.TabPanelItem}.
11142      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11143      */
11144     removeTab : function(id){
11145         var items = this.items;
11146         var tab = items[id];
11147         if(!tab) { return; }
11148         var index = items.indexOf(tab);
11149         if(this.active == tab && items.length > 1){
11150             var newTab = this.getNextAvailable(index);
11151             if(newTab) {
11152                 newTab.activate();
11153             }
11154         }
11155         this.stripEl.dom.removeChild(tab.pnode.dom);
11156         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11157             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11158         }
11159         items.splice(index, 1);
11160         delete this.items[tab.id];
11161         tab.fireEvent("close", tab);
11162         tab.purgeListeners();
11163         this.autoSizeTabs();
11164     },
11165
11166     getNextAvailable : function(start){
11167         var items = this.items;
11168         var index = start;
11169         // look for a next tab that will slide over to
11170         // replace the one being removed
11171         while(index < items.length){
11172             var item = items[++index];
11173             if(item && !item.isHidden()){
11174                 return item;
11175             }
11176         }
11177         // if one isn't found select the previous tab (on the left)
11178         index = start;
11179         while(index >= 0){
11180             var item = items[--index];
11181             if(item && !item.isHidden()){
11182                 return item;
11183             }
11184         }
11185         return null;
11186     },
11187
11188     /**
11189      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11190      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11191      */
11192     disableTab : function(id){
11193         var tab = this.items[id];
11194         if(tab && this.active != tab){
11195             tab.disable();
11196         }
11197     },
11198
11199     /**
11200      * Enables a {@link Roo.TabPanelItem} that is disabled.
11201      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11202      */
11203     enableTab : function(id){
11204         var tab = this.items[id];
11205         tab.enable();
11206     },
11207
11208     /**
11209      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11210      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11211      * @return {Roo.TabPanelItem} The TabPanelItem.
11212      */
11213     activate : function(id){
11214         var tab = this.items[id];
11215         if(!tab){
11216             return null;
11217         }
11218         if(tab == this.active || tab.disabled){
11219             return tab;
11220         }
11221         var e = {};
11222         this.fireEvent("beforetabchange", this, e, tab);
11223         if(e.cancel !== true && !tab.disabled){
11224             if(this.active){
11225                 this.active.hide();
11226             }
11227             this.active = this.items[id];
11228             this.active.show();
11229             this.fireEvent("tabchange", this, this.active);
11230         }
11231         return tab;
11232     },
11233
11234     /**
11235      * Gets the active {@link Roo.TabPanelItem}.
11236      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11237      */
11238     getActiveTab : function(){
11239         return this.active;
11240     },
11241
11242     /**
11243      * Updates the tab body element to fit the height of the container element
11244      * for overflow scrolling
11245      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11246      */
11247     syncHeight : function(targetHeight){
11248         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11249         var bm = this.bodyEl.getMargins();
11250         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11251         this.bodyEl.setHeight(newHeight);
11252         return newHeight;
11253     },
11254
11255     onResize : function(){
11256         if(this.monitorResize){
11257             this.autoSizeTabs();
11258         }
11259     },
11260
11261     /**
11262      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11263      */
11264     beginUpdate : function(){
11265         this.updating = true;
11266     },
11267
11268     /**
11269      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11270      */
11271     endUpdate : function(){
11272         this.updating = false;
11273         this.autoSizeTabs();
11274     },
11275
11276     /**
11277      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11278      */
11279     autoSizeTabs : function(){
11280         var count = this.items.length;
11281         var vcount = count - this.hiddenCount;
11282         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11283         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11284         var availWidth = Math.floor(w / vcount);
11285         var b = this.stripBody;
11286         if(b.getWidth() > w){
11287             var tabs = this.items;
11288             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11289             if(availWidth < this.minTabWidth){
11290                 /*if(!this.sleft){    // incomplete scrolling code
11291                     this.createScrollButtons();
11292                 }
11293                 this.showScroll();
11294                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11295             }
11296         }else{
11297             if(this.currentTabWidth < this.preferredTabWidth){
11298                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11299             }
11300         }
11301     },
11302
11303     /**
11304      * Returns the number of tabs in this TabPanel.
11305      * @return {Number}
11306      */
11307      getCount : function(){
11308          return this.items.length;
11309      },
11310
11311     /**
11312      * Resizes all the tabs to the passed width
11313      * @param {Number} The new width
11314      */
11315     setTabWidth : function(width){
11316         this.currentTabWidth = width;
11317         for(var i = 0, len = this.items.length; i < len; i++) {
11318                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11319         }
11320     },
11321
11322     /**
11323      * Destroys this TabPanel
11324      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11325      */
11326     destroy : function(removeEl){
11327         Roo.EventManager.removeResizeListener(this.onResize, this);
11328         for(var i = 0, len = this.items.length; i < len; i++){
11329             this.items[i].purgeListeners();
11330         }
11331         if(removeEl === true){
11332             this.el.update("");
11333             this.el.remove();
11334         }
11335     }
11336 });
11337
11338 /**
11339  * @class Roo.TabPanelItem
11340  * @extends Roo.util.Observable
11341  * Represents an individual item (tab plus body) in a TabPanel.
11342  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11343  * @param {String} id The id of this TabPanelItem
11344  * @param {String} text The text for the tab of this TabPanelItem
11345  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11346  */
11347 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11348     /**
11349      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11350      * @type Roo.TabPanel
11351      */
11352     this.tabPanel = tabPanel;
11353     /**
11354      * The id for this TabPanelItem
11355      * @type String
11356      */
11357     this.id = id;
11358     /** @private */
11359     this.disabled = false;
11360     /** @private */
11361     this.text = text;
11362     /** @private */
11363     this.loaded = false;
11364     this.closable = closable;
11365
11366     /**
11367      * The body element for this TabPanelItem.
11368      * @type Roo.Element
11369      */
11370     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11371     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11372     this.bodyEl.setStyle("display", "block");
11373     this.bodyEl.setStyle("zoom", "1");
11374     this.hideAction();
11375
11376     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11377     /** @private */
11378     this.el = Roo.get(els.el, true);
11379     this.inner = Roo.get(els.inner, true);
11380     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11381     this.pnode = Roo.get(els.el.parentNode, true);
11382     this.el.on("mousedown", this.onTabMouseDown, this);
11383     this.el.on("click", this.onTabClick, this);
11384     /** @private */
11385     if(closable){
11386         var c = Roo.get(els.close, true);
11387         c.dom.title = this.closeText;
11388         c.addClassOnOver("close-over");
11389         c.on("click", this.closeClick, this);
11390      }
11391
11392     this.addEvents({
11393          /**
11394          * @event activate
11395          * Fires when this tab becomes the active tab.
11396          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11397          * @param {Roo.TabPanelItem} this
11398          */
11399         "activate": true,
11400         /**
11401          * @event beforeclose
11402          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11403          * @param {Roo.TabPanelItem} this
11404          * @param {Object} e Set cancel to true on this object to cancel the close.
11405          */
11406         "beforeclose": true,
11407         /**
11408          * @event close
11409          * Fires when this tab is closed.
11410          * @param {Roo.TabPanelItem} this
11411          */
11412          "close": true,
11413         /**
11414          * @event deactivate
11415          * Fires when this tab is no longer the active tab.
11416          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11417          * @param {Roo.TabPanelItem} this
11418          */
11419          "deactivate" : true
11420     });
11421     this.hidden = false;
11422
11423     Roo.TabPanelItem.superclass.constructor.call(this);
11424 };
11425
11426 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11427     purgeListeners : function(){
11428        Roo.util.Observable.prototype.purgeListeners.call(this);
11429        this.el.removeAllListeners();
11430     },
11431     /**
11432      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11433      */
11434     show : function(){
11435         this.pnode.addClass("on");
11436         this.showAction();
11437         if(Roo.isOpera){
11438             this.tabPanel.stripWrap.repaint();
11439         }
11440         this.fireEvent("activate", this.tabPanel, this);
11441     },
11442
11443     /**
11444      * Returns true if this tab is the active tab.
11445      * @return {Boolean}
11446      */
11447     isActive : function(){
11448         return this.tabPanel.getActiveTab() == this;
11449     },
11450
11451     /**
11452      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11453      */
11454     hide : function(){
11455         this.pnode.removeClass("on");
11456         this.hideAction();
11457         this.fireEvent("deactivate", this.tabPanel, this);
11458     },
11459
11460     hideAction : function(){
11461         this.bodyEl.hide();
11462         this.bodyEl.setStyle("position", "absolute");
11463         this.bodyEl.setLeft("-20000px");
11464         this.bodyEl.setTop("-20000px");
11465     },
11466
11467     showAction : function(){
11468         this.bodyEl.setStyle("position", "relative");
11469         this.bodyEl.setTop("");
11470         this.bodyEl.setLeft("");
11471         this.bodyEl.show();
11472     },
11473
11474     /**
11475      * Set the tooltip for the tab.
11476      * @param {String} tooltip The tab's tooltip
11477      */
11478     setTooltip : function(text){
11479         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11480             this.textEl.dom.qtip = text;
11481             this.textEl.dom.removeAttribute('title');
11482         }else{
11483             this.textEl.dom.title = text;
11484         }
11485     },
11486
11487     onTabClick : function(e){
11488         e.preventDefault();
11489         this.tabPanel.activate(this.id);
11490     },
11491
11492     onTabMouseDown : function(e){
11493         e.preventDefault();
11494         this.tabPanel.activate(this.id);
11495     },
11496
11497     getWidth : function(){
11498         return this.inner.getWidth();
11499     },
11500
11501     setWidth : function(width){
11502         var iwidth = width - this.pnode.getPadding("lr");
11503         this.inner.setWidth(iwidth);
11504         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11505         this.pnode.setWidth(width);
11506     },
11507
11508     /**
11509      * Show or hide the tab
11510      * @param {Boolean} hidden True to hide or false to show.
11511      */
11512     setHidden : function(hidden){
11513         this.hidden = hidden;
11514         this.pnode.setStyle("display", hidden ? "none" : "");
11515     },
11516
11517     /**
11518      * Returns true if this tab is "hidden"
11519      * @return {Boolean}
11520      */
11521     isHidden : function(){
11522         return this.hidden;
11523     },
11524
11525     /**
11526      * Returns the text for this tab
11527      * @return {String}
11528      */
11529     getText : function(){
11530         return this.text;
11531     },
11532
11533     autoSize : function(){
11534         //this.el.beginMeasure();
11535         this.textEl.setWidth(1);
11536         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11537         //this.el.endMeasure();
11538     },
11539
11540     /**
11541      * Sets the text for the tab (Note: this also sets the tooltip text)
11542      * @param {String} text The tab's text and tooltip
11543      */
11544     setText : function(text){
11545         this.text = text;
11546         this.textEl.update(text);
11547         this.setTooltip(text);
11548         if(!this.tabPanel.resizeTabs){
11549             this.autoSize();
11550         }
11551     },
11552     /**
11553      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11554      */
11555     activate : function(){
11556         this.tabPanel.activate(this.id);
11557     },
11558
11559     /**
11560      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11561      */
11562     disable : function(){
11563         if(this.tabPanel.active != this){
11564             this.disabled = true;
11565             this.pnode.addClass("disabled");
11566         }
11567     },
11568
11569     /**
11570      * Enables this TabPanelItem if it was previously disabled.
11571      */
11572     enable : function(){
11573         this.disabled = false;
11574         this.pnode.removeClass("disabled");
11575     },
11576
11577     /**
11578      * Sets the content for this TabPanelItem.
11579      * @param {String} content The content
11580      * @param {Boolean} loadScripts true to look for and load scripts
11581      */
11582     setContent : function(content, loadScripts){
11583         this.bodyEl.update(content, loadScripts);
11584     },
11585
11586     /**
11587      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11588      * @return {Roo.UpdateManager} The UpdateManager
11589      */
11590     getUpdateManager : function(){
11591         return this.bodyEl.getUpdateManager();
11592     },
11593
11594     /**
11595      * Set a URL to be used to load the content for this TabPanelItem.
11596      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11597      * @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)
11598      * @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)
11599      * @return {Roo.UpdateManager} The UpdateManager
11600      */
11601     setUrl : function(url, params, loadOnce){
11602         if(this.refreshDelegate){
11603             this.un('activate', this.refreshDelegate);
11604         }
11605         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11606         this.on("activate", this.refreshDelegate);
11607         return this.bodyEl.getUpdateManager();
11608     },
11609
11610     /** @private */
11611     _handleRefresh : function(url, params, loadOnce){
11612         if(!loadOnce || !this.loaded){
11613             var updater = this.bodyEl.getUpdateManager();
11614             updater.update(url, params, this._setLoaded.createDelegate(this));
11615         }
11616     },
11617
11618     /**
11619      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11620      *   Will fail silently if the setUrl method has not been called.
11621      *   This does not activate the panel, just updates its content.
11622      */
11623     refresh : function(){
11624         if(this.refreshDelegate){
11625            this.loaded = false;
11626            this.refreshDelegate();
11627         }
11628     },
11629
11630     /** @private */
11631     _setLoaded : function(){
11632         this.loaded = true;
11633     },
11634
11635     /** @private */
11636     closeClick : function(e){
11637         var o = {};
11638         e.stopEvent();
11639         this.fireEvent("beforeclose", this, o);
11640         if(o.cancel !== true){
11641             this.tabPanel.removeTab(this.id);
11642         }
11643     },
11644     /**
11645      * The text displayed in the tooltip for the close icon.
11646      * @type String
11647      */
11648     closeText : "Close this tab"
11649 });
11650
11651 /** @private */
11652 Roo.TabPanel.prototype.createStrip = function(container){
11653     var strip = document.createElement("div");
11654     strip.className = "x-tabs-wrap";
11655     container.appendChild(strip);
11656     return strip;
11657 };
11658 /** @private */
11659 Roo.TabPanel.prototype.createStripList = function(strip){
11660     // div wrapper for retard IE
11661     // returns the "tr" element.
11662     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11663         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11664         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11665     return strip.firstChild.firstChild.firstChild.firstChild;
11666 };
11667 /** @private */
11668 Roo.TabPanel.prototype.createBody = function(container){
11669     var body = document.createElement("div");
11670     Roo.id(body, "tab-body");
11671     Roo.fly(body).addClass("x-tabs-body");
11672     container.appendChild(body);
11673     return body;
11674 };
11675 /** @private */
11676 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11677     var body = Roo.getDom(id);
11678     if(!body){
11679         body = document.createElement("div");
11680         body.id = id;
11681     }
11682     Roo.fly(body).addClass("x-tabs-item-body");
11683     bodyEl.insertBefore(body, bodyEl.firstChild);
11684     return body;
11685 };
11686 /** @private */
11687 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11688     var td = document.createElement("td");
11689     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11690     //stripEl.appendChild(td);
11691     if(closable){
11692         td.className = "x-tabs-closable";
11693         if(!this.closeTpl){
11694             this.closeTpl = new Roo.Template(
11695                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11696                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11697                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11698             );
11699         }
11700         var el = this.closeTpl.overwrite(td, {"text": text});
11701         var close = el.getElementsByTagName("div")[0];
11702         var inner = el.getElementsByTagName("em")[0];
11703         return {"el": el, "close": close, "inner": inner};
11704     } else {
11705         if(!this.tabTpl){
11706             this.tabTpl = new Roo.Template(
11707                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11708                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11709             );
11710         }
11711         var el = this.tabTpl.overwrite(td, {"text": text});
11712         var inner = el.getElementsByTagName("em")[0];
11713         return {"el": el, "inner": inner};
11714     }
11715 };/*
11716  * Based on:
11717  * Ext JS Library 1.1.1
11718  * Copyright(c) 2006-2007, Ext JS, LLC.
11719  *
11720  * Originally Released Under LGPL - original licence link has changed is not relivant.
11721  *
11722  * Fork - LGPL
11723  * <script type="text/javascript">
11724  */
11725
11726 /**
11727  * @class Roo.Button
11728  * @extends Roo.util.Observable
11729  * Simple Button class
11730  * @cfg {String} text The button text
11731  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11732  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11733  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11734  * @cfg {Object} scope The scope of the handler
11735  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11736  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11737  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11738  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11739  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11740  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11741    applies if enableToggle = true)
11742  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11743  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11744   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11745  * @constructor
11746  * Create a new button
11747  * @param {Object} config The config object
11748  */
11749 Roo.Button = function(renderTo, config)
11750 {
11751     if (!config) {
11752         config = renderTo;
11753         renderTo = config.renderTo || false;
11754     }
11755     
11756     Roo.apply(this, config);
11757     this.addEvents({
11758         /**
11759              * @event click
11760              * Fires when this button is clicked
11761              * @param {Button} this
11762              * @param {EventObject} e The click event
11763              */
11764             "click" : true,
11765         /**
11766              * @event toggle
11767              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11768              * @param {Button} this
11769              * @param {Boolean} pressed
11770              */
11771             "toggle" : true,
11772         /**
11773              * @event mouseover
11774              * Fires when the mouse hovers over the button
11775              * @param {Button} this
11776              * @param {Event} e The event object
11777              */
11778         'mouseover' : true,
11779         /**
11780              * @event mouseout
11781              * Fires when the mouse exits the button
11782              * @param {Button} this
11783              * @param {Event} e The event object
11784              */
11785         'mouseout': true,
11786          /**
11787              * @event render
11788              * Fires when the button is rendered
11789              * @param {Button} this
11790              */
11791         'render': true
11792     });
11793     if(this.menu){
11794         this.menu = Roo.menu.MenuMgr.get(this.menu);
11795     }
11796     // register listeners first!!  - so render can be captured..
11797     Roo.util.Observable.call(this);
11798     if(renderTo){
11799         this.render(renderTo);
11800     }
11801     
11802   
11803 };
11804
11805 Roo.extend(Roo.Button, Roo.util.Observable, {
11806     /**
11807      * 
11808      */
11809     
11810     /**
11811      * Read-only. True if this button is hidden
11812      * @type Boolean
11813      */
11814     hidden : false,
11815     /**
11816      * Read-only. True if this button is disabled
11817      * @type Boolean
11818      */
11819     disabled : false,
11820     /**
11821      * Read-only. True if this button is pressed (only if enableToggle = true)
11822      * @type Boolean
11823      */
11824     pressed : false,
11825
11826     /**
11827      * @cfg {Number} tabIndex 
11828      * The DOM tabIndex for this button (defaults to undefined)
11829      */
11830     tabIndex : undefined,
11831
11832     /**
11833      * @cfg {Boolean} enableToggle
11834      * True to enable pressed/not pressed toggling (defaults to false)
11835      */
11836     enableToggle: false,
11837     /**
11838      * @cfg {Mixed} menu
11839      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11840      */
11841     menu : undefined,
11842     /**
11843      * @cfg {String} menuAlign
11844      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11845      */
11846     menuAlign : "tl-bl?",
11847
11848     /**
11849      * @cfg {String} iconCls
11850      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11851      */
11852     iconCls : undefined,
11853     /**
11854      * @cfg {String} type
11855      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11856      */
11857     type : 'button',
11858
11859     // private
11860     menuClassTarget: 'tr',
11861
11862     /**
11863      * @cfg {String} clickEvent
11864      * The type of event to map to the button's event handler (defaults to 'click')
11865      */
11866     clickEvent : 'click',
11867
11868     /**
11869      * @cfg {Boolean} handleMouseEvents
11870      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11871      */
11872     handleMouseEvents : true,
11873
11874     /**
11875      * @cfg {String} tooltipType
11876      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11877      */
11878     tooltipType : 'qtip',
11879
11880     /**
11881      * @cfg {String} cls
11882      * A CSS class to apply to the button's main element.
11883      */
11884     
11885     /**
11886      * @cfg {Roo.Template} template (Optional)
11887      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11888      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11889      * require code modifications if required elements (e.g. a button) aren't present.
11890      */
11891
11892     // private
11893     render : function(renderTo){
11894         var btn;
11895         if(this.hideParent){
11896             this.parentEl = Roo.get(renderTo);
11897         }
11898         if(!this.dhconfig){
11899             if(!this.template){
11900                 if(!Roo.Button.buttonTemplate){
11901                     // hideous table template
11902                     Roo.Button.buttonTemplate = new Roo.Template(
11903                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11904                         '<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>',
11905                         "</tr></tbody></table>");
11906                 }
11907                 this.template = Roo.Button.buttonTemplate;
11908             }
11909             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11910             var btnEl = btn.child("button:first");
11911             btnEl.on('focus', this.onFocus, this);
11912             btnEl.on('blur', this.onBlur, this);
11913             if(this.cls){
11914                 btn.addClass(this.cls);
11915             }
11916             if(this.icon){
11917                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11918             }
11919             if(this.iconCls){
11920                 btnEl.addClass(this.iconCls);
11921                 if(!this.cls){
11922                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11923                 }
11924             }
11925             if(this.tabIndex !== undefined){
11926                 btnEl.dom.tabIndex = this.tabIndex;
11927             }
11928             if(this.tooltip){
11929                 if(typeof this.tooltip == 'object'){
11930                     Roo.QuickTips.tips(Roo.apply({
11931                           target: btnEl.id
11932                     }, this.tooltip));
11933                 } else {
11934                     btnEl.dom[this.tooltipType] = this.tooltip;
11935                 }
11936             }
11937         }else{
11938             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11939         }
11940         this.el = btn;
11941         if(this.id){
11942             this.el.dom.id = this.el.id = this.id;
11943         }
11944         if(this.menu){
11945             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11946             this.menu.on("show", this.onMenuShow, this);
11947             this.menu.on("hide", this.onMenuHide, this);
11948         }
11949         btn.addClass("x-btn");
11950         if(Roo.isIE && !Roo.isIE7){
11951             this.autoWidth.defer(1, this);
11952         }else{
11953             this.autoWidth();
11954         }
11955         if(this.handleMouseEvents){
11956             btn.on("mouseover", this.onMouseOver, this);
11957             btn.on("mouseout", this.onMouseOut, this);
11958             btn.on("mousedown", this.onMouseDown, this);
11959         }
11960         btn.on(this.clickEvent, this.onClick, this);
11961         //btn.on("mouseup", this.onMouseUp, this);
11962         if(this.hidden){
11963             this.hide();
11964         }
11965         if(this.disabled){
11966             this.disable();
11967         }
11968         Roo.ButtonToggleMgr.register(this);
11969         if(this.pressed){
11970             this.el.addClass("x-btn-pressed");
11971         }
11972         if(this.repeat){
11973             var repeater = new Roo.util.ClickRepeater(btn,
11974                 typeof this.repeat == "object" ? this.repeat : {}
11975             );
11976             repeater.on("click", this.onClick,  this);
11977         }
11978         
11979         this.fireEvent('render', this);
11980         
11981     },
11982     /**
11983      * Returns the button's underlying element
11984      * @return {Roo.Element} The element
11985      */
11986     getEl : function(){
11987         return this.el;  
11988     },
11989     
11990     /**
11991      * Destroys this Button and removes any listeners.
11992      */
11993     destroy : function(){
11994         Roo.ButtonToggleMgr.unregister(this);
11995         this.el.removeAllListeners();
11996         this.purgeListeners();
11997         this.el.remove();
11998     },
11999
12000     // private
12001     autoWidth : function(){
12002         if(this.el){
12003             this.el.setWidth("auto");
12004             if(Roo.isIE7 && Roo.isStrict){
12005                 var ib = this.el.child('button');
12006                 if(ib && ib.getWidth() > 20){
12007                     ib.clip();
12008                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12009                 }
12010             }
12011             if(this.minWidth){
12012                 if(this.hidden){
12013                     this.el.beginMeasure();
12014                 }
12015                 if(this.el.getWidth() < this.minWidth){
12016                     this.el.setWidth(this.minWidth);
12017                 }
12018                 if(this.hidden){
12019                     this.el.endMeasure();
12020                 }
12021             }
12022         }
12023     },
12024
12025     /**
12026      * Assigns this button's click handler
12027      * @param {Function} handler The function to call when the button is clicked
12028      * @param {Object} scope (optional) Scope for the function passed in
12029      */
12030     setHandler : function(handler, scope){
12031         this.handler = handler;
12032         this.scope = scope;  
12033     },
12034     
12035     /**
12036      * Sets this button's text
12037      * @param {String} text The button text
12038      */
12039     setText : function(text){
12040         this.text = text;
12041         if(this.el){
12042             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12043         }
12044         this.autoWidth();
12045     },
12046     
12047     /**
12048      * Gets the text for this button
12049      * @return {String} The button text
12050      */
12051     getText : function(){
12052         return this.text;  
12053     },
12054     
12055     /**
12056      * Show this button
12057      */
12058     show: function(){
12059         this.hidden = false;
12060         if(this.el){
12061             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12062         }
12063     },
12064     
12065     /**
12066      * Hide this button
12067      */
12068     hide: function(){
12069         this.hidden = true;
12070         if(this.el){
12071             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12072         }
12073     },
12074     
12075     /**
12076      * Convenience function for boolean show/hide
12077      * @param {Boolean} visible True to show, false to hide
12078      */
12079     setVisible: function(visible){
12080         if(visible) {
12081             this.show();
12082         }else{
12083             this.hide();
12084         }
12085     },
12086     
12087     /**
12088      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12089      * @param {Boolean} state (optional) Force a particular state
12090      */
12091     toggle : function(state){
12092         state = state === undefined ? !this.pressed : state;
12093         if(state != this.pressed){
12094             if(state){
12095                 this.el.addClass("x-btn-pressed");
12096                 this.pressed = true;
12097                 this.fireEvent("toggle", this, true);
12098             }else{
12099                 this.el.removeClass("x-btn-pressed");
12100                 this.pressed = false;
12101                 this.fireEvent("toggle", this, false);
12102             }
12103             if(this.toggleHandler){
12104                 this.toggleHandler.call(this.scope || this, this, state);
12105             }
12106         }
12107     },
12108     
12109     /**
12110      * Focus the button
12111      */
12112     focus : function(){
12113         this.el.child('button:first').focus();
12114     },
12115     
12116     /**
12117      * Disable this button
12118      */
12119     disable : function(){
12120         if(this.el){
12121             this.el.addClass("x-btn-disabled");
12122         }
12123         this.disabled = true;
12124     },
12125     
12126     /**
12127      * Enable this button
12128      */
12129     enable : function(){
12130         if(this.el){
12131             this.el.removeClass("x-btn-disabled");
12132         }
12133         this.disabled = false;
12134     },
12135
12136     /**
12137      * Convenience function for boolean enable/disable
12138      * @param {Boolean} enabled True to enable, false to disable
12139      */
12140     setDisabled : function(v){
12141         this[v !== true ? "enable" : "disable"]();
12142     },
12143
12144     // private
12145     onClick : function(e){
12146         if(e){
12147             e.preventDefault();
12148         }
12149         if(e.button != 0){
12150             return;
12151         }
12152         if(!this.disabled){
12153             if(this.enableToggle){
12154                 this.toggle();
12155             }
12156             if(this.menu && !this.menu.isVisible()){
12157                 this.menu.show(this.el, this.menuAlign);
12158             }
12159             this.fireEvent("click", this, e);
12160             if(this.handler){
12161                 this.el.removeClass("x-btn-over");
12162                 this.handler.call(this.scope || this, this, e);
12163             }
12164         }
12165     },
12166     // private
12167     onMouseOver : function(e){
12168         if(!this.disabled){
12169             this.el.addClass("x-btn-over");
12170             this.fireEvent('mouseover', this, e);
12171         }
12172     },
12173     // private
12174     onMouseOut : function(e){
12175         if(!e.within(this.el,  true)){
12176             this.el.removeClass("x-btn-over");
12177             this.fireEvent('mouseout', this, e);
12178         }
12179     },
12180     // private
12181     onFocus : function(e){
12182         if(!this.disabled){
12183             this.el.addClass("x-btn-focus");
12184         }
12185     },
12186     // private
12187     onBlur : function(e){
12188         this.el.removeClass("x-btn-focus");
12189     },
12190     // private
12191     onMouseDown : function(e){
12192         if(!this.disabled && e.button == 0){
12193             this.el.addClass("x-btn-click");
12194             Roo.get(document).on('mouseup', this.onMouseUp, this);
12195         }
12196     },
12197     // private
12198     onMouseUp : function(e){
12199         if(e.button == 0){
12200             this.el.removeClass("x-btn-click");
12201             Roo.get(document).un('mouseup', this.onMouseUp, this);
12202         }
12203     },
12204     // private
12205     onMenuShow : function(e){
12206         this.el.addClass("x-btn-menu-active");
12207     },
12208     // private
12209     onMenuHide : function(e){
12210         this.el.removeClass("x-btn-menu-active");
12211     }   
12212 });
12213
12214 // Private utility class used by Button
12215 Roo.ButtonToggleMgr = function(){
12216    var groups = {};
12217    
12218    function toggleGroup(btn, state){
12219        if(state){
12220            var g = groups[btn.toggleGroup];
12221            for(var i = 0, l = g.length; i < l; i++){
12222                if(g[i] != btn){
12223                    g[i].toggle(false);
12224                }
12225            }
12226        }
12227    }
12228    
12229    return {
12230        register : function(btn){
12231            if(!btn.toggleGroup){
12232                return;
12233            }
12234            var g = groups[btn.toggleGroup];
12235            if(!g){
12236                g = groups[btn.toggleGroup] = [];
12237            }
12238            g.push(btn);
12239            btn.on("toggle", toggleGroup);
12240        },
12241        
12242        unregister : function(btn){
12243            if(!btn.toggleGroup){
12244                return;
12245            }
12246            var g = groups[btn.toggleGroup];
12247            if(g){
12248                g.remove(btn);
12249                btn.un("toggle", toggleGroup);
12250            }
12251        }
12252    };
12253 }();/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263  
12264 /**
12265  * @class Roo.SplitButton
12266  * @extends Roo.Button
12267  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12268  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12269  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12270  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12271  * @cfg {String} arrowTooltip The title attribute of the arrow
12272  * @constructor
12273  * Create a new menu button
12274  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12275  * @param {Object} config The config object
12276  */
12277 Roo.SplitButton = function(renderTo, config){
12278     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12279     /**
12280      * @event arrowclick
12281      * Fires when this button's arrow is clicked
12282      * @param {SplitButton} this
12283      * @param {EventObject} e The click event
12284      */
12285     this.addEvents({"arrowclick":true});
12286 };
12287
12288 Roo.extend(Roo.SplitButton, Roo.Button, {
12289     render : function(renderTo){
12290         // this is one sweet looking template!
12291         var tpl = new Roo.Template(
12292             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12293             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12294             '<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>',
12295             "</tbody></table></td><td>",
12296             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12297             '<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>',
12298             "</tbody></table></td></tr></table>"
12299         );
12300         var btn = tpl.append(renderTo, [this.text, this.type], true);
12301         var btnEl = btn.child("button");
12302         if(this.cls){
12303             btn.addClass(this.cls);
12304         }
12305         if(this.icon){
12306             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12307         }
12308         if(this.iconCls){
12309             btnEl.addClass(this.iconCls);
12310             if(!this.cls){
12311                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12312             }
12313         }
12314         this.el = btn;
12315         if(this.handleMouseEvents){
12316             btn.on("mouseover", this.onMouseOver, this);
12317             btn.on("mouseout", this.onMouseOut, this);
12318             btn.on("mousedown", this.onMouseDown, this);
12319             btn.on("mouseup", this.onMouseUp, this);
12320         }
12321         btn.on(this.clickEvent, this.onClick, this);
12322         if(this.tooltip){
12323             if(typeof this.tooltip == 'object'){
12324                 Roo.QuickTips.tips(Roo.apply({
12325                       target: btnEl.id
12326                 }, this.tooltip));
12327             } else {
12328                 btnEl.dom[this.tooltipType] = this.tooltip;
12329             }
12330         }
12331         if(this.arrowTooltip){
12332             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12333         }
12334         if(this.hidden){
12335             this.hide();
12336         }
12337         if(this.disabled){
12338             this.disable();
12339         }
12340         if(this.pressed){
12341             this.el.addClass("x-btn-pressed");
12342         }
12343         if(Roo.isIE && !Roo.isIE7){
12344             this.autoWidth.defer(1, this);
12345         }else{
12346             this.autoWidth();
12347         }
12348         if(this.menu){
12349             this.menu.on("show", this.onMenuShow, this);
12350             this.menu.on("hide", this.onMenuHide, this);
12351         }
12352         this.fireEvent('render', this);
12353     },
12354
12355     // private
12356     autoWidth : function(){
12357         if(this.el){
12358             var tbl = this.el.child("table:first");
12359             var tbl2 = this.el.child("table:last");
12360             this.el.setWidth("auto");
12361             tbl.setWidth("auto");
12362             if(Roo.isIE7 && Roo.isStrict){
12363                 var ib = this.el.child('button:first');
12364                 if(ib && ib.getWidth() > 20){
12365                     ib.clip();
12366                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12367                 }
12368             }
12369             if(this.minWidth){
12370                 if(this.hidden){
12371                     this.el.beginMeasure();
12372                 }
12373                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12374                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12375                 }
12376                 if(this.hidden){
12377                     this.el.endMeasure();
12378                 }
12379             }
12380             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12381         } 
12382     },
12383     /**
12384      * Sets this button's click handler
12385      * @param {Function} handler The function to call when the button is clicked
12386      * @param {Object} scope (optional) Scope for the function passed above
12387      */
12388     setHandler : function(handler, scope){
12389         this.handler = handler;
12390         this.scope = scope;  
12391     },
12392     
12393     /**
12394      * Sets this button's arrow click handler
12395      * @param {Function} handler The function to call when the arrow is clicked
12396      * @param {Object} scope (optional) Scope for the function passed above
12397      */
12398     setArrowHandler : function(handler, scope){
12399         this.arrowHandler = handler;
12400         this.scope = scope;  
12401     },
12402     
12403     /**
12404      * Focus the button
12405      */
12406     focus : function(){
12407         if(this.el){
12408             this.el.child("button:first").focus();
12409         }
12410     },
12411
12412     // private
12413     onClick : function(e){
12414         e.preventDefault();
12415         if(!this.disabled){
12416             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12417                 if(this.menu && !this.menu.isVisible()){
12418                     this.menu.show(this.el, this.menuAlign);
12419                 }
12420                 this.fireEvent("arrowclick", this, e);
12421                 if(this.arrowHandler){
12422                     this.arrowHandler.call(this.scope || this, this, e);
12423                 }
12424             }else{
12425                 this.fireEvent("click", this, e);
12426                 if(this.handler){
12427                     this.handler.call(this.scope || this, this, e);
12428                 }
12429             }
12430         }
12431     },
12432     // private
12433     onMouseDown : function(e){
12434         if(!this.disabled){
12435             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12436         }
12437     },
12438     // private
12439     onMouseUp : function(e){
12440         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12441     }   
12442 });
12443
12444
12445 // backwards compat
12446 Roo.MenuButton = Roo.SplitButton;/*
12447  * Based on:
12448  * Ext JS Library 1.1.1
12449  * Copyright(c) 2006-2007, Ext JS, LLC.
12450  *
12451  * Originally Released Under LGPL - original licence link has changed is not relivant.
12452  *
12453  * Fork - LGPL
12454  * <script type="text/javascript">
12455  */
12456
12457 /**
12458  * @class Roo.Toolbar
12459  * Basic Toolbar class.
12460  * @constructor
12461  * Creates a new Toolbar
12462  * @param {Object} container The config object
12463  */ 
12464 Roo.Toolbar = function(container, buttons, config)
12465 {
12466     /// old consturctor format still supported..
12467     if(container instanceof Array){ // omit the container for later rendering
12468         buttons = container;
12469         config = buttons;
12470         container = null;
12471     }
12472     if (typeof(container) == 'object' && container.xtype) {
12473         config = container;
12474         container = config.container;
12475         buttons = config.buttons || []; // not really - use items!!
12476     }
12477     var xitems = [];
12478     if (config && config.items) {
12479         xitems = config.items;
12480         delete config.items;
12481     }
12482     Roo.apply(this, config);
12483     this.buttons = buttons;
12484     
12485     if(container){
12486         this.render(container);
12487     }
12488     this.xitems = xitems;
12489     Roo.each(xitems, function(b) {
12490         this.add(b);
12491     }, this);
12492     
12493 };
12494
12495 Roo.Toolbar.prototype = {
12496     /**
12497      * @cfg {Array} items
12498      * array of button configs or elements to add (will be converted to a MixedCollection)
12499      */
12500     
12501     /**
12502      * @cfg {String/HTMLElement/Element} container
12503      * The id or element that will contain the toolbar
12504      */
12505     // private
12506     render : function(ct){
12507         this.el = Roo.get(ct);
12508         if(this.cls){
12509             this.el.addClass(this.cls);
12510         }
12511         // using a table allows for vertical alignment
12512         // 100% width is needed by Safari...
12513         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12514         this.tr = this.el.child("tr", true);
12515         var autoId = 0;
12516         this.items = new Roo.util.MixedCollection(false, function(o){
12517             return o.id || ("item" + (++autoId));
12518         });
12519         if(this.buttons){
12520             this.add.apply(this, this.buttons);
12521             delete this.buttons;
12522         }
12523     },
12524
12525     /**
12526      * Adds element(s) to the toolbar -- this function takes a variable number of 
12527      * arguments of mixed type and adds them to the toolbar.
12528      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12529      * <ul>
12530      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12531      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12532      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12533      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12534      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12535      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12536      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12537      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12538      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12539      * </ul>
12540      * @param {Mixed} arg2
12541      * @param {Mixed} etc.
12542      */
12543     add : function(){
12544         var a = arguments, l = a.length;
12545         for(var i = 0; i < l; i++){
12546             this._add(a[i]);
12547         }
12548     },
12549     // private..
12550     _add : function(el) {
12551         
12552         if (el.xtype) {
12553             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12554         }
12555         
12556         if (el.applyTo){ // some kind of form field
12557             return this.addField(el);
12558         } 
12559         if (el.render){ // some kind of Toolbar.Item
12560             return this.addItem(el);
12561         }
12562         if (typeof el == "string"){ // string
12563             if(el == "separator" || el == "-"){
12564                 return this.addSeparator();
12565             }
12566             if (el == " "){
12567                 return this.addSpacer();
12568             }
12569             if(el == "->"){
12570                 return this.addFill();
12571             }
12572             return this.addText(el);
12573             
12574         }
12575         if(el.tagName){ // element
12576             return this.addElement(el);
12577         }
12578         if(typeof el == "object"){ // must be button config?
12579             return this.addButton(el);
12580         }
12581         // and now what?!?!
12582         return false;
12583         
12584     },
12585     
12586     /**
12587      * Add an Xtype element
12588      * @param {Object} xtype Xtype Object
12589      * @return {Object} created Object
12590      */
12591     addxtype : function(e){
12592         return this.add(e);  
12593     },
12594     
12595     /**
12596      * Returns the Element for this toolbar.
12597      * @return {Roo.Element}
12598      */
12599     getEl : function(){
12600         return this.el;  
12601     },
12602     
12603     /**
12604      * Adds a separator
12605      * @return {Roo.Toolbar.Item} The separator item
12606      */
12607     addSeparator : function(){
12608         return this.addItem(new Roo.Toolbar.Separator());
12609     },
12610
12611     /**
12612      * Adds a spacer element
12613      * @return {Roo.Toolbar.Spacer} The spacer item
12614      */
12615     addSpacer : function(){
12616         return this.addItem(new Roo.Toolbar.Spacer());
12617     },
12618
12619     /**
12620      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12621      * @return {Roo.Toolbar.Fill} The fill item
12622      */
12623     addFill : function(){
12624         return this.addItem(new Roo.Toolbar.Fill());
12625     },
12626
12627     /**
12628      * Adds any standard HTML element to the toolbar
12629      * @param {String/HTMLElement/Element} el The element or id of the element to add
12630      * @return {Roo.Toolbar.Item} The element's item
12631      */
12632     addElement : function(el){
12633         return this.addItem(new Roo.Toolbar.Item(el));
12634     },
12635     /**
12636      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12637      * @type Roo.util.MixedCollection  
12638      */
12639     items : false,
12640      
12641     /**
12642      * Adds any Toolbar.Item or subclass
12643      * @param {Roo.Toolbar.Item} item
12644      * @return {Roo.Toolbar.Item} The item
12645      */
12646     addItem : function(item){
12647         var td = this.nextBlock();
12648         item.render(td);
12649         this.items.add(item);
12650         return item;
12651     },
12652     
12653     /**
12654      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12655      * @param {Object/Array} config A button config or array of configs
12656      * @return {Roo.Toolbar.Button/Array}
12657      */
12658     addButton : function(config){
12659         if(config instanceof Array){
12660             var buttons = [];
12661             for(var i = 0, len = config.length; i < len; i++) {
12662                 buttons.push(this.addButton(config[i]));
12663             }
12664             return buttons;
12665         }
12666         var b = config;
12667         if(!(config instanceof Roo.Toolbar.Button)){
12668             b = config.split ?
12669                 new Roo.Toolbar.SplitButton(config) :
12670                 new Roo.Toolbar.Button(config);
12671         }
12672         var td = this.nextBlock();
12673         b.render(td);
12674         this.items.add(b);
12675         return b;
12676     },
12677     
12678     /**
12679      * Adds text to the toolbar
12680      * @param {String} text The text to add
12681      * @return {Roo.Toolbar.Item} The element's item
12682      */
12683     addText : function(text){
12684         return this.addItem(new Roo.Toolbar.TextItem(text));
12685     },
12686     
12687     /**
12688      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12689      * @param {Number} index The index where the item is to be inserted
12690      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12691      * @return {Roo.Toolbar.Button/Item}
12692      */
12693     insertButton : function(index, item){
12694         if(item instanceof Array){
12695             var buttons = [];
12696             for(var i = 0, len = item.length; i < len; i++) {
12697                buttons.push(this.insertButton(index + i, item[i]));
12698             }
12699             return buttons;
12700         }
12701         if (!(item instanceof Roo.Toolbar.Button)){
12702            item = new Roo.Toolbar.Button(item);
12703         }
12704         var td = document.createElement("td");
12705         this.tr.insertBefore(td, this.tr.childNodes[index]);
12706         item.render(td);
12707         this.items.insert(index, item);
12708         return item;
12709     },
12710     
12711     /**
12712      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12713      * @param {Object} config
12714      * @return {Roo.Toolbar.Item} The element's item
12715      */
12716     addDom : function(config, returnEl){
12717         var td = this.nextBlock();
12718         Roo.DomHelper.overwrite(td, config);
12719         var ti = new Roo.Toolbar.Item(td.firstChild);
12720         ti.render(td);
12721         this.items.add(ti);
12722         return ti;
12723     },
12724
12725     /**
12726      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12727      * @type Roo.util.MixedCollection  
12728      */
12729     fields : false,
12730     
12731     /**
12732      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12733      * Note: the field should not have been rendered yet. For a field that has already been
12734      * rendered, use {@link #addElement}.
12735      * @param {Roo.form.Field} field
12736      * @return {Roo.ToolbarItem}
12737      */
12738      
12739       
12740     addField : function(field) {
12741         if (!this.fields) {
12742             var autoId = 0;
12743             this.fields = new Roo.util.MixedCollection(false, function(o){
12744                 return o.id || ("item" + (++autoId));
12745             });
12746
12747         }
12748         
12749         var td = this.nextBlock();
12750         field.render(td);
12751         var ti = new Roo.Toolbar.Item(td.firstChild);
12752         ti.render(td);
12753         this.items.add(ti);
12754         this.fields.add(field);
12755         return ti;
12756     },
12757     /**
12758      * Hide the toolbar
12759      * @method hide
12760      */
12761      
12762       
12763     hide : function()
12764     {
12765         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12766         this.el.child('div').hide();
12767     },
12768     /**
12769      * Show the toolbar
12770      * @method show
12771      */
12772     show : function()
12773     {
12774         this.el.child('div').show();
12775     },
12776       
12777     // private
12778     nextBlock : function(){
12779         var td = document.createElement("td");
12780         this.tr.appendChild(td);
12781         return td;
12782     },
12783
12784     // private
12785     destroy : function(){
12786         if(this.items){ // rendered?
12787             Roo.destroy.apply(Roo, this.items.items);
12788         }
12789         if(this.fields){ // rendered?
12790             Roo.destroy.apply(Roo, this.fields.items);
12791         }
12792         Roo.Element.uncache(this.el, this.tr);
12793     }
12794 };
12795
12796 /**
12797  * @class Roo.Toolbar.Item
12798  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12799  * @constructor
12800  * Creates a new Item
12801  * @param {HTMLElement} el 
12802  */
12803 Roo.Toolbar.Item = function(el){
12804     this.el = Roo.getDom(el);
12805     this.id = Roo.id(this.el);
12806     this.hidden = false;
12807 };
12808
12809 Roo.Toolbar.Item.prototype = {
12810     
12811     /**
12812      * Get this item's HTML Element
12813      * @return {HTMLElement}
12814      */
12815     getEl : function(){
12816        return this.el;  
12817     },
12818
12819     // private
12820     render : function(td){
12821         this.td = td;
12822         td.appendChild(this.el);
12823     },
12824     
12825     /**
12826      * Removes and destroys this item.
12827      */
12828     destroy : function(){
12829         this.td.parentNode.removeChild(this.td);
12830     },
12831     
12832     /**
12833      * Shows this item.
12834      */
12835     show: function(){
12836         this.hidden = false;
12837         this.td.style.display = "";
12838     },
12839     
12840     /**
12841      * Hides this item.
12842      */
12843     hide: function(){
12844         this.hidden = true;
12845         this.td.style.display = "none";
12846     },
12847     
12848     /**
12849      * Convenience function for boolean show/hide.
12850      * @param {Boolean} visible true to show/false to hide
12851      */
12852     setVisible: function(visible){
12853         if(visible) {
12854             this.show();
12855         }else{
12856             this.hide();
12857         }
12858     },
12859     
12860     /**
12861      * Try to focus this item.
12862      */
12863     focus : function(){
12864         Roo.fly(this.el).focus();
12865     },
12866     
12867     /**
12868      * Disables this item.
12869      */
12870     disable : function(){
12871         Roo.fly(this.td).addClass("x-item-disabled");
12872         this.disabled = true;
12873         this.el.disabled = true;
12874     },
12875     
12876     /**
12877      * Enables this item.
12878      */
12879     enable : function(){
12880         Roo.fly(this.td).removeClass("x-item-disabled");
12881         this.disabled = false;
12882         this.el.disabled = false;
12883     }
12884 };
12885
12886
12887 /**
12888  * @class Roo.Toolbar.Separator
12889  * @extends Roo.Toolbar.Item
12890  * A simple toolbar separator class
12891  * @constructor
12892  * Creates a new Separator
12893  */
12894 Roo.Toolbar.Separator = function(){
12895     var s = document.createElement("span");
12896     s.className = "ytb-sep";
12897     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12898 };
12899 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12900     enable:Roo.emptyFn,
12901     disable:Roo.emptyFn,
12902     focus:Roo.emptyFn
12903 });
12904
12905 /**
12906  * @class Roo.Toolbar.Spacer
12907  * @extends Roo.Toolbar.Item
12908  * A simple element that adds extra horizontal space to a toolbar.
12909  * @constructor
12910  * Creates a new Spacer
12911  */
12912 Roo.Toolbar.Spacer = function(){
12913     var s = document.createElement("div");
12914     s.className = "ytb-spacer";
12915     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12916 };
12917 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12918     enable:Roo.emptyFn,
12919     disable:Roo.emptyFn,
12920     focus:Roo.emptyFn
12921 });
12922
12923 /**
12924  * @class Roo.Toolbar.Fill
12925  * @extends Roo.Toolbar.Spacer
12926  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12927  * @constructor
12928  * Creates a new Spacer
12929  */
12930 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12931     // private
12932     render : function(td){
12933         td.style.width = '100%';
12934         Roo.Toolbar.Fill.superclass.render.call(this, td);
12935     }
12936 });
12937
12938 /**
12939  * @class Roo.Toolbar.TextItem
12940  * @extends Roo.Toolbar.Item
12941  * A simple class that renders text directly into a toolbar.
12942  * @constructor
12943  * Creates a new TextItem
12944  * @param {String} text
12945  */
12946 Roo.Toolbar.TextItem = function(text){
12947     if (typeof(text) == 'object') {
12948         text = text.text;
12949     }
12950     var s = document.createElement("span");
12951     s.className = "ytb-text";
12952     s.innerHTML = text;
12953     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12954 };
12955 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12956     enable:Roo.emptyFn,
12957     disable:Roo.emptyFn,
12958     focus:Roo.emptyFn
12959 });
12960
12961 /**
12962  * @class Roo.Toolbar.Button
12963  * @extends Roo.Button
12964  * A button that renders into a toolbar.
12965  * @constructor
12966  * Creates a new Button
12967  * @param {Object} config A standard {@link Roo.Button} config object
12968  */
12969 Roo.Toolbar.Button = function(config){
12970     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12971 };
12972 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12973     render : function(td){
12974         this.td = td;
12975         Roo.Toolbar.Button.superclass.render.call(this, td);
12976     },
12977     
12978     /**
12979      * Removes and destroys this button
12980      */
12981     destroy : function(){
12982         Roo.Toolbar.Button.superclass.destroy.call(this);
12983         this.td.parentNode.removeChild(this.td);
12984     },
12985     
12986     /**
12987      * Shows this button
12988      */
12989     show: function(){
12990         this.hidden = false;
12991         this.td.style.display = "";
12992     },
12993     
12994     /**
12995      * Hides this button
12996      */
12997     hide: function(){
12998         this.hidden = true;
12999         this.td.style.display = "none";
13000     },
13001
13002     /**
13003      * Disables this item
13004      */
13005     disable : function(){
13006         Roo.fly(this.td).addClass("x-item-disabled");
13007         this.disabled = true;
13008     },
13009
13010     /**
13011      * Enables this item
13012      */
13013     enable : function(){
13014         Roo.fly(this.td).removeClass("x-item-disabled");
13015         this.disabled = false;
13016     }
13017 });
13018 // backwards compat
13019 Roo.ToolbarButton = Roo.Toolbar.Button;
13020
13021 /**
13022  * @class Roo.Toolbar.SplitButton
13023  * @extends Roo.SplitButton
13024  * A menu button that renders into a toolbar.
13025  * @constructor
13026  * Creates a new SplitButton
13027  * @param {Object} config A standard {@link Roo.SplitButton} config object
13028  */
13029 Roo.Toolbar.SplitButton = function(config){
13030     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13031 };
13032 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13033     render : function(td){
13034         this.td = td;
13035         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13036     },
13037     
13038     /**
13039      * Removes and destroys this button
13040      */
13041     destroy : function(){
13042         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13043         this.td.parentNode.removeChild(this.td);
13044     },
13045     
13046     /**
13047      * Shows this button
13048      */
13049     show: function(){
13050         this.hidden = false;
13051         this.td.style.display = "";
13052     },
13053     
13054     /**
13055      * Hides this button
13056      */
13057     hide: function(){
13058         this.hidden = true;
13059         this.td.style.display = "none";
13060     }
13061 });
13062
13063 // backwards compat
13064 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13065  * Based on:
13066  * Ext JS Library 1.1.1
13067  * Copyright(c) 2006-2007, Ext JS, LLC.
13068  *
13069  * Originally Released Under LGPL - original licence link has changed is not relivant.
13070  *
13071  * Fork - LGPL
13072  * <script type="text/javascript">
13073  */
13074  
13075 /**
13076  * @class Roo.PagingToolbar
13077  * @extends Roo.Toolbar
13078  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13079  * @constructor
13080  * Create a new PagingToolbar
13081  * @param {Object} config The config object
13082  */
13083 Roo.PagingToolbar = function(el, ds, config)
13084 {
13085     // old args format still supported... - xtype is prefered..
13086     if (typeof(el) == 'object' && el.xtype) {
13087         // created from xtype...
13088         config = el;
13089         ds = el.dataSource;
13090         el = config.container;
13091     }
13092     var items = [];
13093     if (config.items) {
13094         items = config.items;
13095         config.items = [];
13096     }
13097     
13098     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13099     this.ds = ds;
13100     this.cursor = 0;
13101     this.renderButtons(this.el);
13102     this.bind(ds);
13103     
13104     // supprot items array.
13105    
13106     Roo.each(items, function(e) {
13107         this.add(Roo.factory(e));
13108     },this);
13109     
13110 };
13111
13112 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13113     /**
13114      * @cfg {Roo.data.Store} dataSource
13115      * The underlying data store providing the paged data
13116      */
13117     /**
13118      * @cfg {String/HTMLElement/Element} container
13119      * container The id or element that will contain the toolbar
13120      */
13121     /**
13122      * @cfg {Boolean} displayInfo
13123      * True to display the displayMsg (defaults to false)
13124      */
13125     /**
13126      * @cfg {Number} pageSize
13127      * The number of records to display per page (defaults to 20)
13128      */
13129     pageSize: 20,
13130     /**
13131      * @cfg {String} displayMsg
13132      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13133      */
13134     displayMsg : 'Displaying {0} - {1} of {2}',
13135     /**
13136      * @cfg {String} emptyMsg
13137      * The message to display when no records are found (defaults to "No data to display")
13138      */
13139     emptyMsg : 'No data to display',
13140     /**
13141      * Customizable piece of the default paging text (defaults to "Page")
13142      * @type String
13143      */
13144     beforePageText : "Page",
13145     /**
13146      * Customizable piece of the default paging text (defaults to "of %0")
13147      * @type String
13148      */
13149     afterPageText : "of {0}",
13150     /**
13151      * Customizable piece of the default paging text (defaults to "First Page")
13152      * @type String
13153      */
13154     firstText : "First Page",
13155     /**
13156      * Customizable piece of the default paging text (defaults to "Previous Page")
13157      * @type String
13158      */
13159     prevText : "Previous Page",
13160     /**
13161      * Customizable piece of the default paging text (defaults to "Next Page")
13162      * @type String
13163      */
13164     nextText : "Next Page",
13165     /**
13166      * Customizable piece of the default paging text (defaults to "Last Page")
13167      * @type String
13168      */
13169     lastText : "Last Page",
13170     /**
13171      * Customizable piece of the default paging text (defaults to "Refresh")
13172      * @type String
13173      */
13174     refreshText : "Refresh",
13175
13176     // private
13177     renderButtons : function(el){
13178         Roo.PagingToolbar.superclass.render.call(this, el);
13179         this.first = this.addButton({
13180             tooltip: this.firstText,
13181             cls: "x-btn-icon x-grid-page-first",
13182             disabled: true,
13183             handler: this.onClick.createDelegate(this, ["first"])
13184         });
13185         this.prev = this.addButton({
13186             tooltip: this.prevText,
13187             cls: "x-btn-icon x-grid-page-prev",
13188             disabled: true,
13189             handler: this.onClick.createDelegate(this, ["prev"])
13190         });
13191         //this.addSeparator();
13192         this.add(this.beforePageText);
13193         this.field = Roo.get(this.addDom({
13194            tag: "input",
13195            type: "text",
13196            size: "3",
13197            value: "1",
13198            cls: "x-grid-page-number"
13199         }).el);
13200         this.field.on("keydown", this.onPagingKeydown, this);
13201         this.field.on("focus", function(){this.dom.select();});
13202         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13203         this.field.setHeight(18);
13204         //this.addSeparator();
13205         this.next = this.addButton({
13206             tooltip: this.nextText,
13207             cls: "x-btn-icon x-grid-page-next",
13208             disabled: true,
13209             handler: this.onClick.createDelegate(this, ["next"])
13210         });
13211         this.last = this.addButton({
13212             tooltip: this.lastText,
13213             cls: "x-btn-icon x-grid-page-last",
13214             disabled: true,
13215             handler: this.onClick.createDelegate(this, ["last"])
13216         });
13217         //this.addSeparator();
13218         this.loading = this.addButton({
13219             tooltip: this.refreshText,
13220             cls: "x-btn-icon x-grid-loading",
13221             handler: this.onClick.createDelegate(this, ["refresh"])
13222         });
13223
13224         if(this.displayInfo){
13225             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13226         }
13227     },
13228
13229     // private
13230     updateInfo : function(){
13231         if(this.displayEl){
13232             var count = this.ds.getCount();
13233             var msg = count == 0 ?
13234                 this.emptyMsg :
13235                 String.format(
13236                     this.displayMsg,
13237                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13238                 );
13239             this.displayEl.update(msg);
13240         }
13241     },
13242
13243     // private
13244     onLoad : function(ds, r, o){
13245        this.cursor = o.params ? o.params.start : 0;
13246        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13247
13248        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13249        this.field.dom.value = ap;
13250        this.first.setDisabled(ap == 1);
13251        this.prev.setDisabled(ap == 1);
13252        this.next.setDisabled(ap == ps);
13253        this.last.setDisabled(ap == ps);
13254        this.loading.enable();
13255        this.updateInfo();
13256     },
13257
13258     // private
13259     getPageData : function(){
13260         var total = this.ds.getTotalCount();
13261         return {
13262             total : total,
13263             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13264             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13265         };
13266     },
13267
13268     // private
13269     onLoadError : function(){
13270         this.loading.enable();
13271     },
13272
13273     // private
13274     onPagingKeydown : function(e){
13275         var k = e.getKey();
13276         var d = this.getPageData();
13277         if(k == e.RETURN){
13278             var v = this.field.dom.value, pageNum;
13279             if(!v || isNaN(pageNum = parseInt(v, 10))){
13280                 this.field.dom.value = d.activePage;
13281                 return;
13282             }
13283             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13284             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13285             e.stopEvent();
13286         }
13287         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))
13288         {
13289           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13290           this.field.dom.value = pageNum;
13291           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13292           e.stopEvent();
13293         }
13294         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13295         {
13296           var v = this.field.dom.value, pageNum; 
13297           var increment = (e.shiftKey) ? 10 : 1;
13298           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13299             increment *= -1;
13300           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13301             this.field.dom.value = d.activePage;
13302             return;
13303           }
13304           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13305           {
13306             this.field.dom.value = parseInt(v, 10) + increment;
13307             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13308             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13309           }
13310           e.stopEvent();
13311         }
13312     },
13313
13314     // private
13315     beforeLoad : function(){
13316         if(this.loading){
13317             this.loading.disable();
13318         }
13319     },
13320
13321     // private
13322     onClick : function(which){
13323         var ds = this.ds;
13324         switch(which){
13325             case "first":
13326                 ds.load({params:{start: 0, limit: this.pageSize}});
13327             break;
13328             case "prev":
13329                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13330             break;
13331             case "next":
13332                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13333             break;
13334             case "last":
13335                 var total = ds.getTotalCount();
13336                 var extra = total % this.pageSize;
13337                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13338                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13339             break;
13340             case "refresh":
13341                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13342             break;
13343         }
13344     },
13345
13346     /**
13347      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13348      * @param {Roo.data.Store} store The data store to unbind
13349      */
13350     unbind : function(ds){
13351         ds.un("beforeload", this.beforeLoad, this);
13352         ds.un("load", this.onLoad, this);
13353         ds.un("loadexception", this.onLoadError, this);
13354         ds.un("remove", this.updateInfo, this);
13355         ds.un("add", this.updateInfo, this);
13356         this.ds = undefined;
13357     },
13358
13359     /**
13360      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13361      * @param {Roo.data.Store} store The data store to bind
13362      */
13363     bind : function(ds){
13364         ds.on("beforeload", this.beforeLoad, this);
13365         ds.on("load", this.onLoad, this);
13366         ds.on("loadexception", this.onLoadError, this);
13367         ds.on("remove", this.updateInfo, this);
13368         ds.on("add", this.updateInfo, this);
13369         this.ds = ds;
13370     }
13371 });/*
13372  * Based on:
13373  * Ext JS Library 1.1.1
13374  * Copyright(c) 2006-2007, Ext JS, LLC.
13375  *
13376  * Originally Released Under LGPL - original licence link has changed is not relivant.
13377  *
13378  * Fork - LGPL
13379  * <script type="text/javascript">
13380  */
13381
13382 /**
13383  * @class Roo.Resizable
13384  * @extends Roo.util.Observable
13385  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13386  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13387  * 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
13388  * the element will be wrapped for you automatically.</p>
13389  * <p>Here is the list of valid resize handles:</p>
13390  * <pre>
13391 Value   Description
13392 ------  -------------------
13393  'n'     north
13394  's'     south
13395  'e'     east
13396  'w'     west
13397  'nw'    northwest
13398  'sw'    southwest
13399  'se'    southeast
13400  'ne'    northeast
13401  'hd'    horizontal drag
13402  'all'   all
13403 </pre>
13404  * <p>Here's an example showing the creation of a typical Resizable:</p>
13405  * <pre><code>
13406 var resizer = new Roo.Resizable("element-id", {
13407     handles: 'all',
13408     minWidth: 200,
13409     minHeight: 100,
13410     maxWidth: 500,
13411     maxHeight: 400,
13412     pinned: true
13413 });
13414 resizer.on("resize", myHandler);
13415 </code></pre>
13416  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13417  * resizer.east.setDisplayed(false);</p>
13418  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13419  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13420  * resize operation's new size (defaults to [0, 0])
13421  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13422  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13423  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13424  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13425  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13426  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13427  * @cfg {Number} width The width of the element in pixels (defaults to null)
13428  * @cfg {Number} height The height of the element in pixels (defaults to null)
13429  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13430  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13431  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13432  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13433  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13434  * in favor of the handles config option (defaults to false)
13435  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13436  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13437  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13438  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13439  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13440  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13441  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13442  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13443  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13444  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13445  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13446  * @constructor
13447  * Create a new resizable component
13448  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13449  * @param {Object} config configuration options
13450   */
13451 Roo.Resizable = function(el, config)
13452 {
13453     this.el = Roo.get(el);
13454
13455     if(config && config.wrap){
13456         config.resizeChild = this.el;
13457         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13458         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13459         this.el.setStyle("overflow", "hidden");
13460         this.el.setPositioning(config.resizeChild.getPositioning());
13461         config.resizeChild.clearPositioning();
13462         if(!config.width || !config.height){
13463             var csize = config.resizeChild.getSize();
13464             this.el.setSize(csize.width, csize.height);
13465         }
13466         if(config.pinned && !config.adjustments){
13467             config.adjustments = "auto";
13468         }
13469     }
13470
13471     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13472     this.proxy.unselectable();
13473     this.proxy.enableDisplayMode('block');
13474
13475     Roo.apply(this, config);
13476
13477     if(this.pinned){
13478         this.disableTrackOver = true;
13479         this.el.addClass("x-resizable-pinned");
13480     }
13481     // if the element isn't positioned, make it relative
13482     var position = this.el.getStyle("position");
13483     if(position != "absolute" && position != "fixed"){
13484         this.el.setStyle("position", "relative");
13485     }
13486     if(!this.handles){ // no handles passed, must be legacy style
13487         this.handles = 's,e,se';
13488         if(this.multiDirectional){
13489             this.handles += ',n,w';
13490         }
13491     }
13492     if(this.handles == "all"){
13493         this.handles = "n s e w ne nw se sw";
13494     }
13495     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13496     var ps = Roo.Resizable.positions;
13497     for(var i = 0, len = hs.length; i < len; i++){
13498         if(hs[i] && ps[hs[i]]){
13499             var pos = ps[hs[i]];
13500             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13501         }
13502     }
13503     // legacy
13504     this.corner = this.southeast;
13505     
13506     // updateBox = the box can move..
13507     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13508         this.updateBox = true;
13509     }
13510
13511     this.activeHandle = null;
13512
13513     if(this.resizeChild){
13514         if(typeof this.resizeChild == "boolean"){
13515             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13516         }else{
13517             this.resizeChild = Roo.get(this.resizeChild, true);
13518         }
13519     }
13520     
13521     if(this.adjustments == "auto"){
13522         var rc = this.resizeChild;
13523         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13524         if(rc && (hw || hn)){
13525             rc.position("relative");
13526             rc.setLeft(hw ? hw.el.getWidth() : 0);
13527             rc.setTop(hn ? hn.el.getHeight() : 0);
13528         }
13529         this.adjustments = [
13530             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13531             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13532         ];
13533     }
13534
13535     if(this.draggable){
13536         this.dd = this.dynamic ?
13537             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13538         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13539     }
13540
13541     // public events
13542     this.addEvents({
13543         /**
13544          * @event beforeresize
13545          * Fired before resize is allowed. Set enabled to false to cancel resize.
13546          * @param {Roo.Resizable} this
13547          * @param {Roo.EventObject} e The mousedown event
13548          */
13549         "beforeresize" : true,
13550         /**
13551          * @event resizing
13552          * Fired a resizing.
13553          * @param {Roo.Resizable} this
13554          * @param {Number} x The new x position
13555          * @param {Number} y The new y position
13556          * @param {Number} w The new w width
13557          * @param {Number} h The new h hight
13558          * @param {Roo.EventObject} e The mouseup event
13559          */
13560         "resizing" : true,
13561         /**
13562          * @event resize
13563          * Fired after a resize.
13564          * @param {Roo.Resizable} this
13565          * @param {Number} width The new width
13566          * @param {Number} height The new height
13567          * @param {Roo.EventObject} e The mouseup event
13568          */
13569         "resize" : true
13570     });
13571
13572     if(this.width !== null && this.height !== null){
13573         this.resizeTo(this.width, this.height);
13574     }else{
13575         this.updateChildSize();
13576     }
13577     if(Roo.isIE){
13578         this.el.dom.style.zoom = 1;
13579     }
13580     Roo.Resizable.superclass.constructor.call(this);
13581 };
13582
13583 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13584         resizeChild : false,
13585         adjustments : [0, 0],
13586         minWidth : 5,
13587         minHeight : 5,
13588         maxWidth : 10000,
13589         maxHeight : 10000,
13590         enabled : true,
13591         animate : false,
13592         duration : .35,
13593         dynamic : false,
13594         handles : false,
13595         multiDirectional : false,
13596         disableTrackOver : false,
13597         easing : 'easeOutStrong',
13598         widthIncrement : 0,
13599         heightIncrement : 0,
13600         pinned : false,
13601         width : null,
13602         height : null,
13603         preserveRatio : false,
13604         transparent: false,
13605         minX: 0,
13606         minY: 0,
13607         draggable: false,
13608
13609         /**
13610          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13611          */
13612         constrainTo: undefined,
13613         /**
13614          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13615          */
13616         resizeRegion: undefined,
13617
13618
13619     /**
13620      * Perform a manual resize
13621      * @param {Number} width
13622      * @param {Number} height
13623      */
13624     resizeTo : function(width, height){
13625         this.el.setSize(width, height);
13626         this.updateChildSize();
13627         this.fireEvent("resize", this, width, height, null);
13628     },
13629
13630     // private
13631     startSizing : function(e, handle){
13632         this.fireEvent("beforeresize", this, e);
13633         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13634
13635             if(!this.overlay){
13636                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13637                 this.overlay.unselectable();
13638                 this.overlay.enableDisplayMode("block");
13639                 this.overlay.on("mousemove", this.onMouseMove, this);
13640                 this.overlay.on("mouseup", this.onMouseUp, this);
13641             }
13642             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13643
13644             this.resizing = true;
13645             this.startBox = this.el.getBox();
13646             this.startPoint = e.getXY();
13647             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13648                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13649
13650             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13651             this.overlay.show();
13652
13653             if(this.constrainTo) {
13654                 var ct = Roo.get(this.constrainTo);
13655                 this.resizeRegion = ct.getRegion().adjust(
13656                     ct.getFrameWidth('t'),
13657                     ct.getFrameWidth('l'),
13658                     -ct.getFrameWidth('b'),
13659                     -ct.getFrameWidth('r')
13660                 );
13661             }
13662
13663             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13664             this.proxy.show();
13665             this.proxy.setBox(this.startBox);
13666             if(!this.dynamic){
13667                 this.proxy.setStyle('visibility', 'visible');
13668             }
13669         }
13670     },
13671
13672     // private
13673     onMouseDown : function(handle, e){
13674         if(this.enabled){
13675             e.stopEvent();
13676             this.activeHandle = handle;
13677             this.startSizing(e, handle);
13678         }
13679     },
13680
13681     // private
13682     onMouseUp : function(e){
13683         var size = this.resizeElement();
13684         this.resizing = false;
13685         this.handleOut();
13686         this.overlay.hide();
13687         this.proxy.hide();
13688         this.fireEvent("resize", this, size.width, size.height, e);
13689     },
13690
13691     // private
13692     updateChildSize : function(){
13693         
13694         if(this.resizeChild){
13695             var el = this.el;
13696             var child = this.resizeChild;
13697             var adj = this.adjustments;
13698             if(el.dom.offsetWidth){
13699                 var b = el.getSize(true);
13700                 child.setSize(b.width+adj[0], b.height+adj[1]);
13701             }
13702             // Second call here for IE
13703             // The first call enables instant resizing and
13704             // the second call corrects scroll bars if they
13705             // exist
13706             if(Roo.isIE){
13707                 setTimeout(function(){
13708                     if(el.dom.offsetWidth){
13709                         var b = el.getSize(true);
13710                         child.setSize(b.width+adj[0], b.height+adj[1]);
13711                     }
13712                 }, 10);
13713             }
13714         }
13715     },
13716
13717     // private
13718     snap : function(value, inc, min){
13719         if(!inc || !value) return value;
13720         var newValue = value;
13721         var m = value % inc;
13722         if(m > 0){
13723             if(m > (inc/2)){
13724                 newValue = value + (inc-m);
13725             }else{
13726                 newValue = value - m;
13727             }
13728         }
13729         return Math.max(min, newValue);
13730     },
13731
13732     // private
13733     resizeElement : function(){
13734         var box = this.proxy.getBox();
13735         if(this.updateBox){
13736             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13737         }else{
13738             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13739         }
13740         this.updateChildSize();
13741         if(!this.dynamic){
13742             this.proxy.hide();
13743         }
13744         return box;
13745     },
13746
13747     // private
13748     constrain : function(v, diff, m, mx){
13749         if(v - diff < m){
13750             diff = v - m;
13751         }else if(v - diff > mx){
13752             diff = mx - v;
13753         }
13754         return diff;
13755     },
13756
13757     // private
13758     onMouseMove : function(e){
13759         
13760         if(this.enabled){
13761             try{// try catch so if something goes wrong the user doesn't get hung
13762
13763             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13764                 return;
13765             }
13766
13767             //var curXY = this.startPoint;
13768             var curSize = this.curSize || this.startBox;
13769             var x = this.startBox.x, y = this.startBox.y;
13770             var ox = x, oy = y;
13771             var w = curSize.width, h = curSize.height;
13772             var ow = w, oh = h;
13773             var mw = this.minWidth, mh = this.minHeight;
13774             var mxw = this.maxWidth, mxh = this.maxHeight;
13775             var wi = this.widthIncrement;
13776             var hi = this.heightIncrement;
13777
13778             var eventXY = e.getXY();
13779             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13780             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13781
13782             var pos = this.activeHandle.position;
13783
13784             switch(pos){
13785                 case "east":
13786                     w += diffX;
13787                     w = Math.min(Math.max(mw, w), mxw);
13788                     break;
13789              
13790                 case "south":
13791                     h += diffY;
13792                     h = Math.min(Math.max(mh, h), mxh);
13793                     break;
13794                 case "southeast":
13795                     w += diffX;
13796                     h += diffY;
13797                     w = Math.min(Math.max(mw, w), mxw);
13798                     h = Math.min(Math.max(mh, h), mxh);
13799                     break;
13800                 case "north":
13801                     diffY = this.constrain(h, diffY, mh, mxh);
13802                     y += diffY;
13803                     h -= diffY;
13804                     break;
13805                 case "hdrag":
13806                     
13807                     if (wi) {
13808                         var adiffX = Math.abs(diffX);
13809                         var sub = (adiffX % wi); // how much 
13810                         if (sub > (wi/2)) { // far enough to snap
13811                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13812                         } else {
13813                             // remove difference.. 
13814                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13815                         }
13816                     }
13817                     x += diffX;
13818                     x = Math.max(this.minX, x);
13819                     break;
13820                 case "west":
13821                     diffX = this.constrain(w, diffX, mw, mxw);
13822                     x += diffX;
13823                     w -= diffX;
13824                     break;
13825                 case "northeast":
13826                     w += diffX;
13827                     w = Math.min(Math.max(mw, w), mxw);
13828                     diffY = this.constrain(h, diffY, mh, mxh);
13829                     y += diffY;
13830                     h -= diffY;
13831                     break;
13832                 case "northwest":
13833                     diffX = this.constrain(w, diffX, mw, mxw);
13834                     diffY = this.constrain(h, diffY, mh, mxh);
13835                     y += diffY;
13836                     h -= diffY;
13837                     x += diffX;
13838                     w -= diffX;
13839                     break;
13840                case "southwest":
13841                     diffX = this.constrain(w, diffX, mw, mxw);
13842                     h += diffY;
13843                     h = Math.min(Math.max(mh, h), mxh);
13844                     x += diffX;
13845                     w -= diffX;
13846                     break;
13847             }
13848
13849             var sw = this.snap(w, wi, mw);
13850             var sh = this.snap(h, hi, mh);
13851             if(sw != w || sh != h){
13852                 switch(pos){
13853                     case "northeast":
13854                         y -= sh - h;
13855                     break;
13856                     case "north":
13857                         y -= sh - h;
13858                         break;
13859                     case "southwest":
13860                         x -= sw - w;
13861                     break;
13862                     case "west":
13863                         x -= sw - w;
13864                         break;
13865                     case "northwest":
13866                         x -= sw - w;
13867                         y -= sh - h;
13868                     break;
13869                 }
13870                 w = sw;
13871                 h = sh;
13872             }
13873
13874             if(this.preserveRatio){
13875                 switch(pos){
13876                     case "southeast":
13877                     case "east":
13878                         h = oh * (w/ow);
13879                         h = Math.min(Math.max(mh, h), mxh);
13880                         w = ow * (h/oh);
13881                        break;
13882                     case "south":
13883                         w = ow * (h/oh);
13884                         w = Math.min(Math.max(mw, w), mxw);
13885                         h = oh * (w/ow);
13886                         break;
13887                     case "northeast":
13888                         w = ow * (h/oh);
13889                         w = Math.min(Math.max(mw, w), mxw);
13890                         h = oh * (w/ow);
13891                     break;
13892                     case "north":
13893                         var tw = w;
13894                         w = ow * (h/oh);
13895                         w = Math.min(Math.max(mw, w), mxw);
13896                         h = oh * (w/ow);
13897                         x += (tw - w) / 2;
13898                         break;
13899                     case "southwest":
13900                         h = oh * (w/ow);
13901                         h = Math.min(Math.max(mh, h), mxh);
13902                         var tw = w;
13903                         w = ow * (h/oh);
13904                         x += tw - w;
13905                         break;
13906                     case "west":
13907                         var th = h;
13908                         h = oh * (w/ow);
13909                         h = Math.min(Math.max(mh, h), mxh);
13910                         y += (th - h) / 2;
13911                         var tw = w;
13912                         w = ow * (h/oh);
13913                         x += tw - w;
13914                        break;
13915                     case "northwest":
13916                         var tw = w;
13917                         var th = h;
13918                         h = oh * (w/ow);
13919                         h = Math.min(Math.max(mh, h), mxh);
13920                         w = ow * (h/oh);
13921                         y += th - h;
13922                         x += tw - w;
13923                        break;
13924
13925                 }
13926             }
13927             if (pos == 'hdrag') {
13928                 w = ow;
13929             }
13930             this.proxy.setBounds(x, y, w, h);
13931             if(this.dynamic){
13932                 this.resizeElement();
13933             }
13934             }catch(e){}
13935         }
13936         this.fireEvent("resizing", this, x, y, w, h, e);
13937     },
13938
13939     // private
13940     handleOver : function(){
13941         if(this.enabled){
13942             this.el.addClass("x-resizable-over");
13943         }
13944     },
13945
13946     // private
13947     handleOut : function(){
13948         if(!this.resizing){
13949             this.el.removeClass("x-resizable-over");
13950         }
13951     },
13952
13953     /**
13954      * Returns the element this component is bound to.
13955      * @return {Roo.Element}
13956      */
13957     getEl : function(){
13958         return this.el;
13959     },
13960
13961     /**
13962      * Returns the resizeChild element (or null).
13963      * @return {Roo.Element}
13964      */
13965     getResizeChild : function(){
13966         return this.resizeChild;
13967     },
13968     groupHandler : function()
13969     {
13970         
13971     },
13972     /**
13973      * Destroys this resizable. If the element was wrapped and
13974      * removeEl is not true then the element remains.
13975      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13976      */
13977     destroy : function(removeEl){
13978         this.proxy.remove();
13979         if(this.overlay){
13980             this.overlay.removeAllListeners();
13981             this.overlay.remove();
13982         }
13983         var ps = Roo.Resizable.positions;
13984         for(var k in ps){
13985             if(typeof ps[k] != "function" && this[ps[k]]){
13986                 var h = this[ps[k]];
13987                 h.el.removeAllListeners();
13988                 h.el.remove();
13989             }
13990         }
13991         if(removeEl){
13992             this.el.update("");
13993             this.el.remove();
13994         }
13995     }
13996 });
13997
13998 // private
13999 // hash to map config positions to true positions
14000 Roo.Resizable.positions = {
14001     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
14002     hd: "hdrag"
14003 };
14004
14005 // private
14006 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14007     if(!this.tpl){
14008         // only initialize the template if resizable is used
14009         var tpl = Roo.DomHelper.createTemplate(
14010             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14011         );
14012         tpl.compile();
14013         Roo.Resizable.Handle.prototype.tpl = tpl;
14014     }
14015     this.position = pos;
14016     this.rz = rz;
14017     // show north drag fro topdra
14018     var handlepos = pos == 'hdrag' ? 'north' : pos;
14019     
14020     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14021     if (pos == 'hdrag') {
14022         this.el.setStyle('cursor', 'pointer');
14023     }
14024     this.el.unselectable();
14025     if(transparent){
14026         this.el.setOpacity(0);
14027     }
14028     this.el.on("mousedown", this.onMouseDown, this);
14029     if(!disableTrackOver){
14030         this.el.on("mouseover", this.onMouseOver, this);
14031         this.el.on("mouseout", this.onMouseOut, this);
14032     }
14033 };
14034
14035 // private
14036 Roo.Resizable.Handle.prototype = {
14037     afterResize : function(rz){
14038         // do nothing
14039     },
14040     // private
14041     onMouseDown : function(e){
14042         this.rz.onMouseDown(this, e);
14043     },
14044     // private
14045     onMouseOver : function(e){
14046         this.rz.handleOver(this, e);
14047     },
14048     // private
14049     onMouseOut : function(e){
14050         this.rz.handleOut(this, e);
14051     }
14052 };/*
14053  * Based on:
14054  * Ext JS Library 1.1.1
14055  * Copyright(c) 2006-2007, Ext JS, LLC.
14056  *
14057  * Originally Released Under LGPL - original licence link has changed is not relivant.
14058  *
14059  * Fork - LGPL
14060  * <script type="text/javascript">
14061  */
14062
14063 /**
14064  * @class Roo.Editor
14065  * @extends Roo.Component
14066  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14067  * @constructor
14068  * Create a new Editor
14069  * @param {Roo.form.Field} field The Field object (or descendant)
14070  * @param {Object} config The config object
14071  */
14072 Roo.Editor = function(field, config){
14073     Roo.Editor.superclass.constructor.call(this, config);
14074     this.field = field;
14075     this.addEvents({
14076         /**
14077              * @event beforestartedit
14078              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14079              * false from the handler of this event.
14080              * @param {Editor} this
14081              * @param {Roo.Element} boundEl The underlying element bound to this editor
14082              * @param {Mixed} value The field value being set
14083              */
14084         "beforestartedit" : true,
14085         /**
14086              * @event startedit
14087              * Fires when this editor is displayed
14088              * @param {Roo.Element} boundEl The underlying element bound to this editor
14089              * @param {Mixed} value The starting field value
14090              */
14091         "startedit" : true,
14092         /**
14093              * @event beforecomplete
14094              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14095              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14096              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14097              * event will not fire since no edit actually occurred.
14098              * @param {Editor} this
14099              * @param {Mixed} value The current field value
14100              * @param {Mixed} startValue The original field value
14101              */
14102         "beforecomplete" : true,
14103         /**
14104              * @event complete
14105              * Fires after editing is complete and any changed value has been written to the underlying field.
14106              * @param {Editor} this
14107              * @param {Mixed} value The current field value
14108              * @param {Mixed} startValue The original field value
14109              */
14110         "complete" : true,
14111         /**
14112          * @event specialkey
14113          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14114          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14115          * @param {Roo.form.Field} this
14116          * @param {Roo.EventObject} e The event object
14117          */
14118         "specialkey" : true
14119     });
14120 };
14121
14122 Roo.extend(Roo.Editor, Roo.Component, {
14123     /**
14124      * @cfg {Boolean/String} autosize
14125      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14126      * or "height" to adopt the height only (defaults to false)
14127      */
14128     /**
14129      * @cfg {Boolean} revertInvalid
14130      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14131      * validation fails (defaults to true)
14132      */
14133     /**
14134      * @cfg {Boolean} ignoreNoChange
14135      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14136      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14137      * will never be ignored.
14138      */
14139     /**
14140      * @cfg {Boolean} hideEl
14141      * False to keep the bound element visible while the editor is displayed (defaults to true)
14142      */
14143     /**
14144      * @cfg {Mixed} value
14145      * The data value of the underlying field (defaults to "")
14146      */
14147     value : "",
14148     /**
14149      * @cfg {String} alignment
14150      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14151      */
14152     alignment: "c-c?",
14153     /**
14154      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14155      * for bottom-right shadow (defaults to "frame")
14156      */
14157     shadow : "frame",
14158     /**
14159      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14160      */
14161     constrain : false,
14162     /**
14163      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14164      */
14165     completeOnEnter : false,
14166     /**
14167      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14168      */
14169     cancelOnEsc : false,
14170     /**
14171      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14172      */
14173     updateEl : false,
14174
14175     // private
14176     onRender : function(ct, position){
14177         this.el = new Roo.Layer({
14178             shadow: this.shadow,
14179             cls: "x-editor",
14180             parentEl : ct,
14181             shim : this.shim,
14182             shadowOffset:4,
14183             id: this.id,
14184             constrain: this.constrain
14185         });
14186         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14187         if(this.field.msgTarget != 'title'){
14188             this.field.msgTarget = 'qtip';
14189         }
14190         this.field.render(this.el);
14191         if(Roo.isGecko){
14192             this.field.el.dom.setAttribute('autocomplete', 'off');
14193         }
14194         this.field.on("specialkey", this.onSpecialKey, this);
14195         if(this.swallowKeys){
14196             this.field.el.swallowEvent(['keydown','keypress']);
14197         }
14198         this.field.show();
14199         this.field.on("blur", this.onBlur, this);
14200         if(this.field.grow){
14201             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14202         }
14203     },
14204
14205     onSpecialKey : function(field, e)
14206     {
14207         //Roo.log('editor onSpecialKey');
14208         if(this.completeOnEnter && e.getKey() == e.ENTER){
14209             e.stopEvent();
14210             this.completeEdit();
14211             return;
14212         }
14213         // do not fire special key otherwise it might hide close the editor...
14214         if(e.getKey() == e.ENTER){    
14215             return;
14216         }
14217         if(this.cancelOnEsc && e.getKey() == e.ESC){
14218             this.cancelEdit();
14219             return;
14220         } 
14221         this.fireEvent('specialkey', field, e);
14222     
14223     },
14224
14225     /**
14226      * Starts the editing process and shows the editor.
14227      * @param {String/HTMLElement/Element} el The element to edit
14228      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14229       * to the innerHTML of el.
14230      */
14231     startEdit : function(el, value){
14232         if(this.editing){
14233             this.completeEdit();
14234         }
14235         this.boundEl = Roo.get(el);
14236         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14237         if(!this.rendered){
14238             this.render(this.parentEl || document.body);
14239         }
14240         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14241             return;
14242         }
14243         this.startValue = v;
14244         this.field.setValue(v);
14245         if(this.autoSize){
14246             var sz = this.boundEl.getSize();
14247             switch(this.autoSize){
14248                 case "width":
14249                 this.setSize(sz.width,  "");
14250                 break;
14251                 case "height":
14252                 this.setSize("",  sz.height);
14253                 break;
14254                 default:
14255                 this.setSize(sz.width,  sz.height);
14256             }
14257         }
14258         this.el.alignTo(this.boundEl, this.alignment);
14259         this.editing = true;
14260         if(Roo.QuickTips){
14261             Roo.QuickTips.disable();
14262         }
14263         this.show();
14264     },
14265
14266     /**
14267      * Sets the height and width of this editor.
14268      * @param {Number} width The new width
14269      * @param {Number} height The new height
14270      */
14271     setSize : function(w, h){
14272         this.field.setSize(w, h);
14273         if(this.el){
14274             this.el.sync();
14275         }
14276     },
14277
14278     /**
14279      * Realigns the editor to the bound field based on the current alignment config value.
14280      */
14281     realign : function(){
14282         this.el.alignTo(this.boundEl, this.alignment);
14283     },
14284
14285     /**
14286      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14287      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14288      */
14289     completeEdit : function(remainVisible){
14290         if(!this.editing){
14291             return;
14292         }
14293         var v = this.getValue();
14294         if(this.revertInvalid !== false && !this.field.isValid()){
14295             v = this.startValue;
14296             this.cancelEdit(true);
14297         }
14298         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14299             this.editing = false;
14300             this.hide();
14301             return;
14302         }
14303         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14304             this.editing = false;
14305             if(this.updateEl && this.boundEl){
14306                 this.boundEl.update(v);
14307             }
14308             if(remainVisible !== true){
14309                 this.hide();
14310             }
14311             this.fireEvent("complete", this, v, this.startValue);
14312         }
14313     },
14314
14315     // private
14316     onShow : function(){
14317         this.el.show();
14318         if(this.hideEl !== false){
14319             this.boundEl.hide();
14320         }
14321         this.field.show();
14322         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14323             this.fixIEFocus = true;
14324             this.deferredFocus.defer(50, this);
14325         }else{
14326             this.field.focus();
14327         }
14328         this.fireEvent("startedit", this.boundEl, this.startValue);
14329     },
14330
14331     deferredFocus : function(){
14332         if(this.editing){
14333             this.field.focus();
14334         }
14335     },
14336
14337     /**
14338      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14339      * reverted to the original starting value.
14340      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14341      * cancel (defaults to false)
14342      */
14343     cancelEdit : function(remainVisible){
14344         if(this.editing){
14345             this.setValue(this.startValue);
14346             if(remainVisible !== true){
14347                 this.hide();
14348             }
14349         }
14350     },
14351
14352     // private
14353     onBlur : function(){
14354         if(this.allowBlur !== true && this.editing){
14355             this.completeEdit();
14356         }
14357     },
14358
14359     // private
14360     onHide : function(){
14361         if(this.editing){
14362             this.completeEdit();
14363             return;
14364         }
14365         this.field.blur();
14366         if(this.field.collapse){
14367             this.field.collapse();
14368         }
14369         this.el.hide();
14370         if(this.hideEl !== false){
14371             this.boundEl.show();
14372         }
14373         if(Roo.QuickTips){
14374             Roo.QuickTips.enable();
14375         }
14376     },
14377
14378     /**
14379      * Sets the data value of the editor
14380      * @param {Mixed} value Any valid value supported by the underlying field
14381      */
14382     setValue : function(v){
14383         this.field.setValue(v);
14384     },
14385
14386     /**
14387      * Gets the data value of the editor
14388      * @return {Mixed} The data value
14389      */
14390     getValue : function(){
14391         return this.field.getValue();
14392     }
14393 });/*
14394  * Based on:
14395  * Ext JS Library 1.1.1
14396  * Copyright(c) 2006-2007, Ext JS, LLC.
14397  *
14398  * Originally Released Under LGPL - original licence link has changed is not relivant.
14399  *
14400  * Fork - LGPL
14401  * <script type="text/javascript">
14402  */
14403  
14404 /**
14405  * @class Roo.BasicDialog
14406  * @extends Roo.util.Observable
14407  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14408  * <pre><code>
14409 var dlg = new Roo.BasicDialog("my-dlg", {
14410     height: 200,
14411     width: 300,
14412     minHeight: 100,
14413     minWidth: 150,
14414     modal: true,
14415     proxyDrag: true,
14416     shadow: true
14417 });
14418 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14419 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14420 dlg.addButton('Cancel', dlg.hide, dlg);
14421 dlg.show();
14422 </code></pre>
14423   <b>A Dialog should always be a direct child of the body element.</b>
14424  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14425  * @cfg {String} title Default text to display in the title bar (defaults to null)
14426  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14427  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14428  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14429  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14430  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14431  * (defaults to null with no animation)
14432  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14433  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14434  * property for valid values (defaults to 'all')
14435  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14436  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14437  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14438  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14439  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14440  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14441  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14442  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14443  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14444  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14445  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14446  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14447  * draggable = true (defaults to false)
14448  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14449  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14450  * shadow (defaults to false)
14451  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14452  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14453  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14454  * @cfg {Array} buttons Array of buttons
14455  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14456  * @constructor
14457  * Create a new BasicDialog.
14458  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14459  * @param {Object} config Configuration options
14460  */
14461 Roo.BasicDialog = function(el, config){
14462     this.el = Roo.get(el);
14463     var dh = Roo.DomHelper;
14464     if(!this.el && config && config.autoCreate){
14465         if(typeof config.autoCreate == "object"){
14466             if(!config.autoCreate.id){
14467                 config.autoCreate.id = el;
14468             }
14469             this.el = dh.append(document.body,
14470                         config.autoCreate, true);
14471         }else{
14472             this.el = dh.append(document.body,
14473                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14474         }
14475     }
14476     el = this.el;
14477     el.setDisplayed(true);
14478     el.hide = this.hideAction;
14479     this.id = el.id;
14480     el.addClass("x-dlg");
14481
14482     Roo.apply(this, config);
14483
14484     this.proxy = el.createProxy("x-dlg-proxy");
14485     this.proxy.hide = this.hideAction;
14486     this.proxy.setOpacity(.5);
14487     this.proxy.hide();
14488
14489     if(config.width){
14490         el.setWidth(config.width);
14491     }
14492     if(config.height){
14493         el.setHeight(config.height);
14494     }
14495     this.size = el.getSize();
14496     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14497         this.xy = [config.x,config.y];
14498     }else{
14499         this.xy = el.getCenterXY(true);
14500     }
14501     /** The header element @type Roo.Element */
14502     this.header = el.child("> .x-dlg-hd");
14503     /** The body element @type Roo.Element */
14504     this.body = el.child("> .x-dlg-bd");
14505     /** The footer element @type Roo.Element */
14506     this.footer = el.child("> .x-dlg-ft");
14507
14508     if(!this.header){
14509         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14510     }
14511     if(!this.body){
14512         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14513     }
14514
14515     this.header.unselectable();
14516     if(this.title){
14517         this.header.update(this.title);
14518     }
14519     // this element allows the dialog to be focused for keyboard event
14520     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14521     this.focusEl.swallowEvent("click", true);
14522
14523     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14524
14525     // wrap the body and footer for special rendering
14526     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14527     if(this.footer){
14528         this.bwrap.dom.appendChild(this.footer.dom);
14529     }
14530
14531     this.bg = this.el.createChild({
14532         tag: "div", cls:"x-dlg-bg",
14533         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14534     });
14535     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14536
14537
14538     if(this.autoScroll !== false && !this.autoTabs){
14539         this.body.setStyle("overflow", "auto");
14540     }
14541
14542     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14543
14544     if(this.closable !== false){
14545         this.el.addClass("x-dlg-closable");
14546         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14547         this.close.on("click", this.closeClick, this);
14548         this.close.addClassOnOver("x-dlg-close-over");
14549     }
14550     if(this.collapsible !== false){
14551         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14552         this.collapseBtn.on("click", this.collapseClick, this);
14553         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14554         this.header.on("dblclick", this.collapseClick, this);
14555     }
14556     if(this.resizable !== false){
14557         this.el.addClass("x-dlg-resizable");
14558         this.resizer = new Roo.Resizable(el, {
14559             minWidth: this.minWidth || 80,
14560             minHeight:this.minHeight || 80,
14561             handles: this.resizeHandles || "all",
14562             pinned: true
14563         });
14564         this.resizer.on("beforeresize", this.beforeResize, this);
14565         this.resizer.on("resize", this.onResize, this);
14566     }
14567     if(this.draggable !== false){
14568         el.addClass("x-dlg-draggable");
14569         if (!this.proxyDrag) {
14570             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14571         }
14572         else {
14573             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14574         }
14575         dd.setHandleElId(this.header.id);
14576         dd.endDrag = this.endMove.createDelegate(this);
14577         dd.startDrag = this.startMove.createDelegate(this);
14578         dd.onDrag = this.onDrag.createDelegate(this);
14579         dd.scroll = false;
14580         this.dd = dd;
14581     }
14582     if(this.modal){
14583         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14584         this.mask.enableDisplayMode("block");
14585         this.mask.hide();
14586         this.el.addClass("x-dlg-modal");
14587     }
14588     if(this.shadow){
14589         this.shadow = new Roo.Shadow({
14590             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14591             offset : this.shadowOffset
14592         });
14593     }else{
14594         this.shadowOffset = 0;
14595     }
14596     if(Roo.useShims && this.shim !== false){
14597         this.shim = this.el.createShim();
14598         this.shim.hide = this.hideAction;
14599         this.shim.hide();
14600     }else{
14601         this.shim = false;
14602     }
14603     if(this.autoTabs){
14604         this.initTabs();
14605     }
14606     if (this.buttons) { 
14607         var bts= this.buttons;
14608         this.buttons = [];
14609         Roo.each(bts, function(b) {
14610             this.addButton(b);
14611         }, this);
14612     }
14613     
14614     
14615     this.addEvents({
14616         /**
14617          * @event keydown
14618          * Fires when a key is pressed
14619          * @param {Roo.BasicDialog} this
14620          * @param {Roo.EventObject} e
14621          */
14622         "keydown" : true,
14623         /**
14624          * @event move
14625          * Fires when this dialog is moved by the user.
14626          * @param {Roo.BasicDialog} this
14627          * @param {Number} x The new page X
14628          * @param {Number} y The new page Y
14629          */
14630         "move" : true,
14631         /**
14632          * @event resize
14633          * Fires when this dialog is resized by the user.
14634          * @param {Roo.BasicDialog} this
14635          * @param {Number} width The new width
14636          * @param {Number} height The new height
14637          */
14638         "resize" : true,
14639         /**
14640          * @event beforehide
14641          * Fires before this dialog is hidden.
14642          * @param {Roo.BasicDialog} this
14643          */
14644         "beforehide" : true,
14645         /**
14646          * @event hide
14647          * Fires when this dialog is hidden.
14648          * @param {Roo.BasicDialog} this
14649          */
14650         "hide" : true,
14651         /**
14652          * @event beforeshow
14653          * Fires before this dialog is shown.
14654          * @param {Roo.BasicDialog} this
14655          */
14656         "beforeshow" : true,
14657         /**
14658          * @event show
14659          * Fires when this dialog is shown.
14660          * @param {Roo.BasicDialog} this
14661          */
14662         "show" : true
14663     });
14664     el.on("keydown", this.onKeyDown, this);
14665     el.on("mousedown", this.toFront, this);
14666     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14667     this.el.hide();
14668     Roo.DialogManager.register(this);
14669     Roo.BasicDialog.superclass.constructor.call(this);
14670 };
14671
14672 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14673     shadowOffset: Roo.isIE ? 6 : 5,
14674     minHeight: 80,
14675     minWidth: 200,
14676     minButtonWidth: 75,
14677     defaultButton: null,
14678     buttonAlign: "right",
14679     tabTag: 'div',
14680     firstShow: true,
14681
14682     /**
14683      * Sets the dialog title text
14684      * @param {String} text The title text to display
14685      * @return {Roo.BasicDialog} this
14686      */
14687     setTitle : function(text){
14688         this.header.update(text);
14689         return this;
14690     },
14691
14692     // private
14693     closeClick : function(){
14694         this.hide();
14695     },
14696
14697     // private
14698     collapseClick : function(){
14699         this[this.collapsed ? "expand" : "collapse"]();
14700     },
14701
14702     /**
14703      * Collapses the dialog to its minimized state (only the title bar is visible).
14704      * Equivalent to the user clicking the collapse dialog button.
14705      */
14706     collapse : function(){
14707         if(!this.collapsed){
14708             this.collapsed = true;
14709             this.el.addClass("x-dlg-collapsed");
14710             this.restoreHeight = this.el.getHeight();
14711             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14712         }
14713     },
14714
14715     /**
14716      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14717      * clicking the expand dialog button.
14718      */
14719     expand : function(){
14720         if(this.collapsed){
14721             this.collapsed = false;
14722             this.el.removeClass("x-dlg-collapsed");
14723             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14724         }
14725     },
14726
14727     /**
14728      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14729      * @return {Roo.TabPanel} The tabs component
14730      */
14731     initTabs : function(){
14732         var tabs = this.getTabs();
14733         while(tabs.getTab(0)){
14734             tabs.removeTab(0);
14735         }
14736         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14737             var dom = el.dom;
14738             tabs.addTab(Roo.id(dom), dom.title);
14739             dom.title = "";
14740         });
14741         tabs.activate(0);
14742         return tabs;
14743     },
14744
14745     // private
14746     beforeResize : function(){
14747         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14748     },
14749
14750     // private
14751     onResize : function(){
14752         this.refreshSize();
14753         this.syncBodyHeight();
14754         this.adjustAssets();
14755         this.focus();
14756         this.fireEvent("resize", this, this.size.width, this.size.height);
14757     },
14758
14759     // private
14760     onKeyDown : function(e){
14761         if(this.isVisible()){
14762             this.fireEvent("keydown", this, e);
14763         }
14764     },
14765
14766     /**
14767      * Resizes the dialog.
14768      * @param {Number} width
14769      * @param {Number} height
14770      * @return {Roo.BasicDialog} this
14771      */
14772     resizeTo : function(width, height){
14773         this.el.setSize(width, height);
14774         this.size = {width: width, height: height};
14775         this.syncBodyHeight();
14776         if(this.fixedcenter){
14777             this.center();
14778         }
14779         if(this.isVisible()){
14780             this.constrainXY();
14781             this.adjustAssets();
14782         }
14783         this.fireEvent("resize", this, width, height);
14784         return this;
14785     },
14786
14787
14788     /**
14789      * Resizes the dialog to fit the specified content size.
14790      * @param {Number} width
14791      * @param {Number} height
14792      * @return {Roo.BasicDialog} this
14793      */
14794     setContentSize : function(w, h){
14795         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14796         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14797         //if(!this.el.isBorderBox()){
14798             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14799             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14800         //}
14801         if(this.tabs){
14802             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14803             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14804         }
14805         this.resizeTo(w, h);
14806         return this;
14807     },
14808
14809     /**
14810      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14811      * executed in response to a particular key being pressed while the dialog is active.
14812      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14813      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14814      * @param {Function} fn The function to call
14815      * @param {Object} scope (optional) The scope of the function
14816      * @return {Roo.BasicDialog} this
14817      */
14818     addKeyListener : function(key, fn, scope){
14819         var keyCode, shift, ctrl, alt;
14820         if(typeof key == "object" && !(key instanceof Array)){
14821             keyCode = key["key"];
14822             shift = key["shift"];
14823             ctrl = key["ctrl"];
14824             alt = key["alt"];
14825         }else{
14826             keyCode = key;
14827         }
14828         var handler = function(dlg, e){
14829             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14830                 var k = e.getKey();
14831                 if(keyCode instanceof Array){
14832                     for(var i = 0, len = keyCode.length; i < len; i++){
14833                         if(keyCode[i] == k){
14834                           fn.call(scope || window, dlg, k, e);
14835                           return;
14836                         }
14837                     }
14838                 }else{
14839                     if(k == keyCode){
14840                         fn.call(scope || window, dlg, k, e);
14841                     }
14842                 }
14843             }
14844         };
14845         this.on("keydown", handler);
14846         return this;
14847     },
14848
14849     /**
14850      * Returns the TabPanel component (creates it if it doesn't exist).
14851      * Note: If you wish to simply check for the existence of tabs without creating them,
14852      * check for a null 'tabs' property.
14853      * @return {Roo.TabPanel} The tabs component
14854      */
14855     getTabs : function(){
14856         if(!this.tabs){
14857             this.el.addClass("x-dlg-auto-tabs");
14858             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14859             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14860         }
14861         return this.tabs;
14862     },
14863
14864     /**
14865      * Adds a button to the footer section of the dialog.
14866      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14867      * object or a valid Roo.DomHelper element config
14868      * @param {Function} handler The function called when the button is clicked
14869      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14870      * @return {Roo.Button} The new button
14871      */
14872     addButton : function(config, handler, scope){
14873         var dh = Roo.DomHelper;
14874         if(!this.footer){
14875             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14876         }
14877         if(!this.btnContainer){
14878             var tb = this.footer.createChild({
14879
14880                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14881                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14882             }, null, true);
14883             this.btnContainer = tb.firstChild.firstChild.firstChild;
14884         }
14885         var bconfig = {
14886             handler: handler,
14887             scope: scope,
14888             minWidth: this.minButtonWidth,
14889             hideParent:true
14890         };
14891         if(typeof config == "string"){
14892             bconfig.text = config;
14893         }else{
14894             if(config.tag){
14895                 bconfig.dhconfig = config;
14896             }else{
14897                 Roo.apply(bconfig, config);
14898             }
14899         }
14900         var fc = false;
14901         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14902             bconfig.position = Math.max(0, bconfig.position);
14903             fc = this.btnContainer.childNodes[bconfig.position];
14904         }
14905          
14906         var btn = new Roo.Button(
14907             fc ? 
14908                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14909                 : this.btnContainer.appendChild(document.createElement("td")),
14910             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14911             bconfig
14912         );
14913         this.syncBodyHeight();
14914         if(!this.buttons){
14915             /**
14916              * Array of all the buttons that have been added to this dialog via addButton
14917              * @type Array
14918              */
14919             this.buttons = [];
14920         }
14921         this.buttons.push(btn);
14922         return btn;
14923     },
14924
14925     /**
14926      * Sets the default button to be focused when the dialog is displayed.
14927      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14928      * @return {Roo.BasicDialog} this
14929      */
14930     setDefaultButton : function(btn){
14931         this.defaultButton = btn;
14932         return this;
14933     },
14934
14935     // private
14936     getHeaderFooterHeight : function(safe){
14937         var height = 0;
14938         if(this.header){
14939            height += this.header.getHeight();
14940         }
14941         if(this.footer){
14942            var fm = this.footer.getMargins();
14943             height += (this.footer.getHeight()+fm.top+fm.bottom);
14944         }
14945         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14946         height += this.centerBg.getPadding("tb");
14947         return height;
14948     },
14949
14950     // private
14951     syncBodyHeight : function()
14952     {
14953         var bd = this.body, // the text
14954             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14955             bw = this.bwrap;
14956         var height = this.size.height - this.getHeaderFooterHeight(false);
14957         bd.setHeight(height-bd.getMargins("tb"));
14958         var hh = this.header.getHeight();
14959         var h = this.size.height-hh;
14960         cb.setHeight(h);
14961         
14962         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14963         bw.setHeight(h-cb.getPadding("tb"));
14964         
14965         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14966         bd.setWidth(bw.getWidth(true));
14967         if(this.tabs){
14968             this.tabs.syncHeight();
14969             if(Roo.isIE){
14970                 this.tabs.el.repaint();
14971             }
14972         }
14973     },
14974
14975     /**
14976      * Restores the previous state of the dialog if Roo.state is configured.
14977      * @return {Roo.BasicDialog} this
14978      */
14979     restoreState : function(){
14980         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14981         if(box && box.width){
14982             this.xy = [box.x, box.y];
14983             this.resizeTo(box.width, box.height);
14984         }
14985         return this;
14986     },
14987
14988     // private
14989     beforeShow : function(){
14990         this.expand();
14991         if(this.fixedcenter){
14992             this.xy = this.el.getCenterXY(true);
14993         }
14994         if(this.modal){
14995             Roo.get(document.body).addClass("x-body-masked");
14996             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14997             this.mask.show();
14998         }
14999         this.constrainXY();
15000     },
15001
15002     // private
15003     animShow : function(){
15004         var b = Roo.get(this.animateTarget).getBox();
15005         this.proxy.setSize(b.width, b.height);
15006         this.proxy.setLocation(b.x, b.y);
15007         this.proxy.show();
15008         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15009                     true, .35, this.showEl.createDelegate(this));
15010     },
15011
15012     /**
15013      * Shows the dialog.
15014      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15015      * @return {Roo.BasicDialog} this
15016      */
15017     show : function(animateTarget){
15018         if (this.fireEvent("beforeshow", this) === false){
15019             return;
15020         }
15021         if(this.syncHeightBeforeShow){
15022             this.syncBodyHeight();
15023         }else if(this.firstShow){
15024             this.firstShow = false;
15025             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15026         }
15027         this.animateTarget = animateTarget || this.animateTarget;
15028         if(!this.el.isVisible()){
15029             this.beforeShow();
15030             if(this.animateTarget && Roo.get(this.animateTarget)){
15031                 this.animShow();
15032             }else{
15033                 this.showEl();
15034             }
15035         }
15036         return this;
15037     },
15038
15039     // private
15040     showEl : function(){
15041         this.proxy.hide();
15042         this.el.setXY(this.xy);
15043         this.el.show();
15044         this.adjustAssets(true);
15045         this.toFront();
15046         this.focus();
15047         // IE peekaboo bug - fix found by Dave Fenwick
15048         if(Roo.isIE){
15049             this.el.repaint();
15050         }
15051         this.fireEvent("show", this);
15052     },
15053
15054     /**
15055      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15056      * dialog itself will receive focus.
15057      */
15058     focus : function(){
15059         if(this.defaultButton){
15060             this.defaultButton.focus();
15061         }else{
15062             this.focusEl.focus();
15063         }
15064     },
15065
15066     // private
15067     constrainXY : function(){
15068         if(this.constraintoviewport !== false){
15069             if(!this.viewSize){
15070                 if(this.container){
15071                     var s = this.container.getSize();
15072                     this.viewSize = [s.width, s.height];
15073                 }else{
15074                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15075                 }
15076             }
15077             var s = Roo.get(this.container||document).getScroll();
15078
15079             var x = this.xy[0], y = this.xy[1];
15080             var w = this.size.width, h = this.size.height;
15081             var vw = this.viewSize[0], vh = this.viewSize[1];
15082             // only move it if it needs it
15083             var moved = false;
15084             // first validate right/bottom
15085             if(x + w > vw+s.left){
15086                 x = vw - w;
15087                 moved = true;
15088             }
15089             if(y + h > vh+s.top){
15090                 y = vh - h;
15091                 moved = true;
15092             }
15093             // then make sure top/left isn't negative
15094             if(x < s.left){
15095                 x = s.left;
15096                 moved = true;
15097             }
15098             if(y < s.top){
15099                 y = s.top;
15100                 moved = true;
15101             }
15102             if(moved){
15103                 // cache xy
15104                 this.xy = [x, y];
15105                 if(this.isVisible()){
15106                     this.el.setLocation(x, y);
15107                     this.adjustAssets();
15108                 }
15109             }
15110         }
15111     },
15112
15113     // private
15114     onDrag : function(){
15115         if(!this.proxyDrag){
15116             this.xy = this.el.getXY();
15117             this.adjustAssets();
15118         }
15119     },
15120
15121     // private
15122     adjustAssets : function(doShow){
15123         var x = this.xy[0], y = this.xy[1];
15124         var w = this.size.width, h = this.size.height;
15125         if(doShow === true){
15126             if(this.shadow){
15127                 this.shadow.show(this.el);
15128             }
15129             if(this.shim){
15130                 this.shim.show();
15131             }
15132         }
15133         if(this.shadow && this.shadow.isVisible()){
15134             this.shadow.show(this.el);
15135         }
15136         if(this.shim && this.shim.isVisible()){
15137             this.shim.setBounds(x, y, w, h);
15138         }
15139     },
15140
15141     // private
15142     adjustViewport : function(w, h){
15143         if(!w || !h){
15144             w = Roo.lib.Dom.getViewWidth();
15145             h = Roo.lib.Dom.getViewHeight();
15146         }
15147         // cache the size
15148         this.viewSize = [w, h];
15149         if(this.modal && this.mask.isVisible()){
15150             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15151             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15152         }
15153         if(this.isVisible()){
15154             this.constrainXY();
15155         }
15156     },
15157
15158     /**
15159      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15160      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15161      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15162      */
15163     destroy : function(removeEl){
15164         if(this.isVisible()){
15165             this.animateTarget = null;
15166             this.hide();
15167         }
15168         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15169         if(this.tabs){
15170             this.tabs.destroy(removeEl);
15171         }
15172         Roo.destroy(
15173              this.shim,
15174              this.proxy,
15175              this.resizer,
15176              this.close,
15177              this.mask
15178         );
15179         if(this.dd){
15180             this.dd.unreg();
15181         }
15182         if(this.buttons){
15183            for(var i = 0, len = this.buttons.length; i < len; i++){
15184                this.buttons[i].destroy();
15185            }
15186         }
15187         this.el.removeAllListeners();
15188         if(removeEl === true){
15189             this.el.update("");
15190             this.el.remove();
15191         }
15192         Roo.DialogManager.unregister(this);
15193     },
15194
15195     // private
15196     startMove : function(){
15197         if(this.proxyDrag){
15198             this.proxy.show();
15199         }
15200         if(this.constraintoviewport !== false){
15201             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15202         }
15203     },
15204
15205     // private
15206     endMove : function(){
15207         if(!this.proxyDrag){
15208             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15209         }else{
15210             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15211             this.proxy.hide();
15212         }
15213         this.refreshSize();
15214         this.adjustAssets();
15215         this.focus();
15216         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15217     },
15218
15219     /**
15220      * Brings this dialog to the front of any other visible dialogs
15221      * @return {Roo.BasicDialog} this
15222      */
15223     toFront : function(){
15224         Roo.DialogManager.bringToFront(this);
15225         return this;
15226     },
15227
15228     /**
15229      * Sends this dialog to the back (under) of any other visible dialogs
15230      * @return {Roo.BasicDialog} this
15231      */
15232     toBack : function(){
15233         Roo.DialogManager.sendToBack(this);
15234         return this;
15235     },
15236
15237     /**
15238      * Centers this dialog in the viewport
15239      * @return {Roo.BasicDialog} this
15240      */
15241     center : function(){
15242         var xy = this.el.getCenterXY(true);
15243         this.moveTo(xy[0], xy[1]);
15244         return this;
15245     },
15246
15247     /**
15248      * Moves the dialog's top-left corner to the specified point
15249      * @param {Number} x
15250      * @param {Number} y
15251      * @return {Roo.BasicDialog} this
15252      */
15253     moveTo : function(x, y){
15254         this.xy = [x,y];
15255         if(this.isVisible()){
15256             this.el.setXY(this.xy);
15257             this.adjustAssets();
15258         }
15259         return this;
15260     },
15261
15262     /**
15263      * Aligns the dialog to the specified element
15264      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15265      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15266      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15267      * @return {Roo.BasicDialog} this
15268      */
15269     alignTo : function(element, position, offsets){
15270         this.xy = this.el.getAlignToXY(element, position, offsets);
15271         if(this.isVisible()){
15272             this.el.setXY(this.xy);
15273             this.adjustAssets();
15274         }
15275         return this;
15276     },
15277
15278     /**
15279      * Anchors an element to another element and realigns it when the window is resized.
15280      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15281      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15282      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15283      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15284      * is a number, it is used as the buffer delay (defaults to 50ms).
15285      * @return {Roo.BasicDialog} this
15286      */
15287     anchorTo : function(el, alignment, offsets, monitorScroll){
15288         var action = function(){
15289             this.alignTo(el, alignment, offsets);
15290         };
15291         Roo.EventManager.onWindowResize(action, this);
15292         var tm = typeof monitorScroll;
15293         if(tm != 'undefined'){
15294             Roo.EventManager.on(window, 'scroll', action, this,
15295                 {buffer: tm == 'number' ? monitorScroll : 50});
15296         }
15297         action.call(this);
15298         return this;
15299     },
15300
15301     /**
15302      * Returns true if the dialog is visible
15303      * @return {Boolean}
15304      */
15305     isVisible : function(){
15306         return this.el.isVisible();
15307     },
15308
15309     // private
15310     animHide : function(callback){
15311         var b = Roo.get(this.animateTarget).getBox();
15312         this.proxy.show();
15313         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15314         this.el.hide();
15315         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15316                     this.hideEl.createDelegate(this, [callback]));
15317     },
15318
15319     /**
15320      * Hides the dialog.
15321      * @param {Function} callback (optional) Function to call when the dialog is hidden
15322      * @return {Roo.BasicDialog} this
15323      */
15324     hide : function(callback){
15325         if (this.fireEvent("beforehide", this) === false){
15326             return;
15327         }
15328         if(this.shadow){
15329             this.shadow.hide();
15330         }
15331         if(this.shim) {
15332           this.shim.hide();
15333         }
15334         // sometimes animateTarget seems to get set.. causing problems...
15335         // this just double checks..
15336         if(this.animateTarget && Roo.get(this.animateTarget)) {
15337            this.animHide(callback);
15338         }else{
15339             this.el.hide();
15340             this.hideEl(callback);
15341         }
15342         return this;
15343     },
15344
15345     // private
15346     hideEl : function(callback){
15347         this.proxy.hide();
15348         if(this.modal){
15349             this.mask.hide();
15350             Roo.get(document.body).removeClass("x-body-masked");
15351         }
15352         this.fireEvent("hide", this);
15353         if(typeof callback == "function"){
15354             callback();
15355         }
15356     },
15357
15358     // private
15359     hideAction : function(){
15360         this.setLeft("-10000px");
15361         this.setTop("-10000px");
15362         this.setStyle("visibility", "hidden");
15363     },
15364
15365     // private
15366     refreshSize : function(){
15367         this.size = this.el.getSize();
15368         this.xy = this.el.getXY();
15369         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15370     },
15371
15372     // private
15373     // z-index is managed by the DialogManager and may be overwritten at any time
15374     setZIndex : function(index){
15375         if(this.modal){
15376             this.mask.setStyle("z-index", index);
15377         }
15378         if(this.shim){
15379             this.shim.setStyle("z-index", ++index);
15380         }
15381         if(this.shadow){
15382             this.shadow.setZIndex(++index);
15383         }
15384         this.el.setStyle("z-index", ++index);
15385         if(this.proxy){
15386             this.proxy.setStyle("z-index", ++index);
15387         }
15388         if(this.resizer){
15389             this.resizer.proxy.setStyle("z-index", ++index);
15390         }
15391
15392         this.lastZIndex = index;
15393     },
15394
15395     /**
15396      * Returns the element for this dialog
15397      * @return {Roo.Element} The underlying dialog Element
15398      */
15399     getEl : function(){
15400         return this.el;
15401     }
15402 });
15403
15404 /**
15405  * @class Roo.DialogManager
15406  * Provides global access to BasicDialogs that have been created and
15407  * support for z-indexing (layering) multiple open dialogs.
15408  */
15409 Roo.DialogManager = function(){
15410     var list = {};
15411     var accessList = [];
15412     var front = null;
15413
15414     // private
15415     var sortDialogs = function(d1, d2){
15416         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15417     };
15418
15419     // private
15420     var orderDialogs = function(){
15421         accessList.sort(sortDialogs);
15422         var seed = Roo.DialogManager.zseed;
15423         for(var i = 0, len = accessList.length; i < len; i++){
15424             var dlg = accessList[i];
15425             if(dlg){
15426                 dlg.setZIndex(seed + (i*10));
15427             }
15428         }
15429     };
15430
15431     return {
15432         /**
15433          * The starting z-index for BasicDialogs (defaults to 9000)
15434          * @type Number The z-index value
15435          */
15436         zseed : 9000,
15437
15438         // private
15439         register : function(dlg){
15440             list[dlg.id] = dlg;
15441             accessList.push(dlg);
15442         },
15443
15444         // private
15445         unregister : function(dlg){
15446             delete list[dlg.id];
15447             var i=0;
15448             var len=0;
15449             if(!accessList.indexOf){
15450                 for(  i = 0, len = accessList.length; i < len; i++){
15451                     if(accessList[i] == dlg){
15452                         accessList.splice(i, 1);
15453                         return;
15454                     }
15455                 }
15456             }else{
15457                  i = accessList.indexOf(dlg);
15458                 if(i != -1){
15459                     accessList.splice(i, 1);
15460                 }
15461             }
15462         },
15463
15464         /**
15465          * Gets a registered dialog by id
15466          * @param {String/Object} id The id of the dialog or a dialog
15467          * @return {Roo.BasicDialog} this
15468          */
15469         get : function(id){
15470             return typeof id == "object" ? id : list[id];
15471         },
15472
15473         /**
15474          * Brings the specified dialog to the front
15475          * @param {String/Object} dlg The id of the dialog or a dialog
15476          * @return {Roo.BasicDialog} this
15477          */
15478         bringToFront : function(dlg){
15479             dlg = this.get(dlg);
15480             if(dlg != front){
15481                 front = dlg;
15482                 dlg._lastAccess = new Date().getTime();
15483                 orderDialogs();
15484             }
15485             return dlg;
15486         },
15487
15488         /**
15489          * Sends the specified dialog to the back
15490          * @param {String/Object} dlg The id of the dialog or a dialog
15491          * @return {Roo.BasicDialog} this
15492          */
15493         sendToBack : function(dlg){
15494             dlg = this.get(dlg);
15495             dlg._lastAccess = -(new Date().getTime());
15496             orderDialogs();
15497             return dlg;
15498         },
15499
15500         /**
15501          * Hides all dialogs
15502          */
15503         hideAll : function(){
15504             for(var id in list){
15505                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15506                     list[id].hide();
15507                 }
15508             }
15509         }
15510     };
15511 }();
15512
15513 /**
15514  * @class Roo.LayoutDialog
15515  * @extends Roo.BasicDialog
15516  * Dialog which provides adjustments for working with a layout in a Dialog.
15517  * Add your necessary layout config options to the dialog's config.<br>
15518  * Example usage (including a nested layout):
15519  * <pre><code>
15520 if(!dialog){
15521     dialog = new Roo.LayoutDialog("download-dlg", {
15522         modal: true,
15523         width:600,
15524         height:450,
15525         shadow:true,
15526         minWidth:500,
15527         minHeight:350,
15528         autoTabs:true,
15529         proxyDrag:true,
15530         // layout config merges with the dialog config
15531         center:{
15532             tabPosition: "top",
15533             alwaysShowTabs: true
15534         }
15535     });
15536     dialog.addKeyListener(27, dialog.hide, dialog);
15537     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15538     dialog.addButton("Build It!", this.getDownload, this);
15539
15540     // we can even add nested layouts
15541     var innerLayout = new Roo.BorderLayout("dl-inner", {
15542         east: {
15543             initialSize: 200,
15544             autoScroll:true,
15545             split:true
15546         },
15547         center: {
15548             autoScroll:true
15549         }
15550     });
15551     innerLayout.beginUpdate();
15552     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15553     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15554     innerLayout.endUpdate(true);
15555
15556     var layout = dialog.getLayout();
15557     layout.beginUpdate();
15558     layout.add("center", new Roo.ContentPanel("standard-panel",
15559                         {title: "Download the Source", fitToFrame:true}));
15560     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15561                {title: "Build your own roo.js"}));
15562     layout.getRegion("center").showPanel(sp);
15563     layout.endUpdate();
15564 }
15565 </code></pre>
15566     * @constructor
15567     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15568     * @param {Object} config configuration options
15569   */
15570 Roo.LayoutDialog = function(el, cfg){
15571     
15572     var config=  cfg;
15573     if (typeof(cfg) == 'undefined') {
15574         config = Roo.apply({}, el);
15575         // not sure why we use documentElement here.. - it should always be body.
15576         // IE7 borks horribly if we use documentElement.
15577         // webkit also does not like documentElement - it creates a body element...
15578         el = Roo.get( document.body || document.documentElement ).createChild();
15579         //config.autoCreate = true;
15580     }
15581     
15582     
15583     config.autoTabs = false;
15584     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15585     this.body.setStyle({overflow:"hidden", position:"relative"});
15586     this.layout = new Roo.BorderLayout(this.body.dom, config);
15587     this.layout.monitorWindowResize = false;
15588     this.el.addClass("x-dlg-auto-layout");
15589     // fix case when center region overwrites center function
15590     this.center = Roo.BasicDialog.prototype.center;
15591     this.on("show", this.layout.layout, this.layout, true);
15592     if (config.items) {
15593         var xitems = config.items;
15594         delete config.items;
15595         Roo.each(xitems, this.addxtype, this);
15596     }
15597     
15598     
15599 };
15600 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15601     /**
15602      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15603      * @deprecated
15604      */
15605     endUpdate : function(){
15606         this.layout.endUpdate();
15607     },
15608
15609     /**
15610      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15611      *  @deprecated
15612      */
15613     beginUpdate : function(){
15614         this.layout.beginUpdate();
15615     },
15616
15617     /**
15618      * Get the BorderLayout for this dialog
15619      * @return {Roo.BorderLayout}
15620      */
15621     getLayout : function(){
15622         return this.layout;
15623     },
15624
15625     showEl : function(){
15626         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15627         if(Roo.isIE7){
15628             this.layout.layout();
15629         }
15630     },
15631
15632     // private
15633     // Use the syncHeightBeforeShow config option to control this automatically
15634     syncBodyHeight : function(){
15635         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15636         if(this.layout){this.layout.layout();}
15637     },
15638     
15639       /**
15640      * Add an xtype element (actually adds to the layout.)
15641      * @return {Object} xdata xtype object data.
15642      */
15643     
15644     addxtype : function(c) {
15645         return this.layout.addxtype(c);
15646     }
15647 });/*
15648  * Based on:
15649  * Ext JS Library 1.1.1
15650  * Copyright(c) 2006-2007, Ext JS, LLC.
15651  *
15652  * Originally Released Under LGPL - original licence link has changed is not relivant.
15653  *
15654  * Fork - LGPL
15655  * <script type="text/javascript">
15656  */
15657  
15658 /**
15659  * @class Roo.MessageBox
15660  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15661  * Example usage:
15662  *<pre><code>
15663 // Basic alert:
15664 Roo.Msg.alert('Status', 'Changes saved successfully.');
15665
15666 // Prompt for user data:
15667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15668     if (btn == 'ok'){
15669         // process text value...
15670     }
15671 });
15672
15673 // Show a dialog using config options:
15674 Roo.Msg.show({
15675    title:'Save Changes?',
15676    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15677    buttons: Roo.Msg.YESNOCANCEL,
15678    fn: processResult,
15679    animEl: 'elId'
15680 });
15681 </code></pre>
15682  * @singleton
15683  */
15684 Roo.MessageBox = function(){
15685     var dlg, opt, mask, waitTimer;
15686     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15687     var buttons, activeTextEl, bwidth;
15688
15689     // private
15690     var handleButton = function(button){
15691         dlg.hide();
15692         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15693     };
15694
15695     // private
15696     var handleHide = function(){
15697         if(opt && opt.cls){
15698             dlg.el.removeClass(opt.cls);
15699         }
15700         if(waitTimer){
15701             Roo.TaskMgr.stop(waitTimer);
15702             waitTimer = null;
15703         }
15704     };
15705
15706     // private
15707     var updateButtons = function(b){
15708         var width = 0;
15709         if(!b){
15710             buttons["ok"].hide();
15711             buttons["cancel"].hide();
15712             buttons["yes"].hide();
15713             buttons["no"].hide();
15714             dlg.footer.dom.style.display = 'none';
15715             return width;
15716         }
15717         dlg.footer.dom.style.display = '';
15718         for(var k in buttons){
15719             if(typeof buttons[k] != "function"){
15720                 if(b[k]){
15721                     buttons[k].show();
15722                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15723                     width += buttons[k].el.getWidth()+15;
15724                 }else{
15725                     buttons[k].hide();
15726                 }
15727             }
15728         }
15729         return width;
15730     };
15731
15732     // private
15733     var handleEsc = function(d, k, e){
15734         if(opt && opt.closable !== false){
15735             dlg.hide();
15736         }
15737         if(e){
15738             e.stopEvent();
15739         }
15740     };
15741
15742     return {
15743         /**
15744          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15745          * @return {Roo.BasicDialog} The BasicDialog element
15746          */
15747         getDialog : function(){
15748            if(!dlg){
15749                 dlg = new Roo.BasicDialog("x-msg-box", {
15750                     autoCreate : true,
15751                     shadow: true,
15752                     draggable: true,
15753                     resizable:false,
15754                     constraintoviewport:false,
15755                     fixedcenter:true,
15756                     collapsible : false,
15757                     shim:true,
15758                     modal: true,
15759                     width:400, height:100,
15760                     buttonAlign:"center",
15761                     closeClick : function(){
15762                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15763                             handleButton("no");
15764                         }else{
15765                             handleButton("cancel");
15766                         }
15767                     }
15768                 });
15769                 dlg.on("hide", handleHide);
15770                 mask = dlg.mask;
15771                 dlg.addKeyListener(27, handleEsc);
15772                 buttons = {};
15773                 var bt = this.buttonText;
15774                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15775                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15776                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15777                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15778                 bodyEl = dlg.body.createChild({
15779
15780                     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>'
15781                 });
15782                 msgEl = bodyEl.dom.firstChild;
15783                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15784                 textboxEl.enableDisplayMode();
15785                 textboxEl.addKeyListener([10,13], function(){
15786                     if(dlg.isVisible() && opt && opt.buttons){
15787                         if(opt.buttons.ok){
15788                             handleButton("ok");
15789                         }else if(opt.buttons.yes){
15790                             handleButton("yes");
15791                         }
15792                     }
15793                 });
15794                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15795                 textareaEl.enableDisplayMode();
15796                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15797                 progressEl.enableDisplayMode();
15798                 var pf = progressEl.dom.firstChild;
15799                 if (pf) {
15800                     pp = Roo.get(pf.firstChild);
15801                     pp.setHeight(pf.offsetHeight);
15802                 }
15803                 
15804             }
15805             return dlg;
15806         },
15807
15808         /**
15809          * Updates the message box body text
15810          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15811          * the XHTML-compliant non-breaking space character '&amp;#160;')
15812          * @return {Roo.MessageBox} This message box
15813          */
15814         updateText : function(text){
15815             if(!dlg.isVisible() && !opt.width){
15816                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15817             }
15818             msgEl.innerHTML = text || '&#160;';
15819       
15820             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15821             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15822             var w = Math.max(
15823                     Math.min(opt.width || cw , this.maxWidth), 
15824                     Math.max(opt.minWidth || this.minWidth, bwidth)
15825             );
15826             if(opt.prompt){
15827                 activeTextEl.setWidth(w);
15828             }
15829             if(dlg.isVisible()){
15830                 dlg.fixedcenter = false;
15831             }
15832             // to big, make it scroll. = But as usual stupid IE does not support
15833             // !important..
15834             
15835             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15836                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15837                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15838             } else {
15839                 bodyEl.dom.style.height = '';
15840                 bodyEl.dom.style.overflowY = '';
15841             }
15842             if (cw > w) {
15843                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15844             } else {
15845                 bodyEl.dom.style.overflowX = '';
15846             }
15847             
15848             dlg.setContentSize(w, bodyEl.getHeight());
15849             if(dlg.isVisible()){
15850                 dlg.fixedcenter = true;
15851             }
15852             return this;
15853         },
15854
15855         /**
15856          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15857          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15858          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15859          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15860          * @return {Roo.MessageBox} This message box
15861          */
15862         updateProgress : function(value, text){
15863             if(text){
15864                 this.updateText(text);
15865             }
15866             if (pp) { // weird bug on my firefox - for some reason this is not defined
15867                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15868             }
15869             return this;
15870         },        
15871
15872         /**
15873          * Returns true if the message box is currently displayed
15874          * @return {Boolean} True if the message box is visible, else false
15875          */
15876         isVisible : function(){
15877             return dlg && dlg.isVisible();  
15878         },
15879
15880         /**
15881          * Hides the message box if it is displayed
15882          */
15883         hide : function(){
15884             if(this.isVisible()){
15885                 dlg.hide();
15886             }  
15887         },
15888
15889         /**
15890          * Displays a new message box, or reinitializes an existing message box, based on the config options
15891          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15892          * The following config object properties are supported:
15893          * <pre>
15894 Property    Type             Description
15895 ----------  ---------------  ------------------------------------------------------------------------------------
15896 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15897                                    closes (defaults to undefined)
15898 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15899                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15900 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15901                                    progress and wait dialogs will ignore this property and always hide the
15902                                    close button as they can only be closed programmatically.
15903 cls               String           A custom CSS class to apply to the message box element
15904 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15905                                    displayed (defaults to 75)
15906 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15907                                    function will be btn (the name of the button that was clicked, if applicable,
15908                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15909                                    Progress and wait dialogs will ignore this option since they do not respond to
15910                                    user actions and can only be closed programmatically, so any required function
15911                                    should be called by the same code after it closes the dialog.
15912 icon              String           A CSS class that provides a background image to be used as an icon for
15913                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15914 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15915 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15916 modal             Boolean          False to allow user interaction with the page while the message box is
15917                                    displayed (defaults to true)
15918 msg               String           A string that will replace the existing message box body text (defaults
15919                                    to the XHTML-compliant non-breaking space character '&#160;')
15920 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15921 progress          Boolean          True to display a progress bar (defaults to false)
15922 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15923 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15924 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15925 title             String           The title text
15926 value             String           The string value to set into the active textbox element if displayed
15927 wait              Boolean          True to display a progress bar (defaults to false)
15928 width             Number           The width of the dialog in pixels
15929 </pre>
15930          *
15931          * Example usage:
15932          * <pre><code>
15933 Roo.Msg.show({
15934    title: 'Address',
15935    msg: 'Please enter your address:',
15936    width: 300,
15937    buttons: Roo.MessageBox.OKCANCEL,
15938    multiline: true,
15939    fn: saveAddress,
15940    animEl: 'addAddressBtn'
15941 });
15942 </code></pre>
15943          * @param {Object} config Configuration options
15944          * @return {Roo.MessageBox} This message box
15945          */
15946         show : function(options)
15947         {
15948             
15949             // this causes nightmares if you show one dialog after another
15950             // especially on callbacks..
15951              
15952             if(this.isVisible()){
15953                 
15954                 this.hide();
15955                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15956                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15957                 Roo.log("New Dialog Message:" +  options.msg )
15958                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15959                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15960                 
15961             }
15962             var d = this.getDialog();
15963             opt = options;
15964             d.setTitle(opt.title || "&#160;");
15965             d.close.setDisplayed(opt.closable !== false);
15966             activeTextEl = textboxEl;
15967             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15968             if(opt.prompt){
15969                 if(opt.multiline){
15970                     textboxEl.hide();
15971                     textareaEl.show();
15972                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15973                         opt.multiline : this.defaultTextHeight);
15974                     activeTextEl = textareaEl;
15975                 }else{
15976                     textboxEl.show();
15977                     textareaEl.hide();
15978                 }
15979             }else{
15980                 textboxEl.hide();
15981                 textareaEl.hide();
15982             }
15983             progressEl.setDisplayed(opt.progress === true);
15984             this.updateProgress(0);
15985             activeTextEl.dom.value = opt.value || "";
15986             if(opt.prompt){
15987                 dlg.setDefaultButton(activeTextEl);
15988             }else{
15989                 var bs = opt.buttons;
15990                 var db = null;
15991                 if(bs && bs.ok){
15992                     db = buttons["ok"];
15993                 }else if(bs && bs.yes){
15994                     db = buttons["yes"];
15995                 }
15996                 dlg.setDefaultButton(db);
15997             }
15998             bwidth = updateButtons(opt.buttons);
15999             this.updateText(opt.msg);
16000             if(opt.cls){
16001                 d.el.addClass(opt.cls);
16002             }
16003             d.proxyDrag = opt.proxyDrag === true;
16004             d.modal = opt.modal !== false;
16005             d.mask = opt.modal !== false ? mask : false;
16006             if(!d.isVisible()){
16007                 // force it to the end of the z-index stack so it gets a cursor in FF
16008                 document.body.appendChild(dlg.el.dom);
16009                 d.animateTarget = null;
16010                 d.show(options.animEl);
16011             }
16012             return this;
16013         },
16014
16015         /**
16016          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
16017          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16018          * and closing the message box when the process is complete.
16019          * @param {String} title The title bar text
16020          * @param {String} msg The message box body text
16021          * @return {Roo.MessageBox} This message box
16022          */
16023         progress : function(title, msg){
16024             this.show({
16025                 title : title,
16026                 msg : msg,
16027                 buttons: false,
16028                 progress:true,
16029                 closable:false,
16030                 minWidth: this.minProgressWidth,
16031                 modal : true
16032             });
16033             return this;
16034         },
16035
16036         /**
16037          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16038          * If a callback function is passed it will be called after the user clicks the button, and the
16039          * id of the button that was clicked will be passed as the only parameter to the callback
16040          * (could also be the top-right close button).
16041          * @param {String} title The title bar text
16042          * @param {String} msg The message box body text
16043          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16044          * @param {Object} scope (optional) The scope of the callback function
16045          * @return {Roo.MessageBox} This message box
16046          */
16047         alert : function(title, msg, fn, scope){
16048             this.show({
16049                 title : title,
16050                 msg : msg,
16051                 buttons: this.OK,
16052                 fn: fn,
16053                 scope : scope,
16054                 modal : true
16055             });
16056             return this;
16057         },
16058
16059         /**
16060          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16061          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16062          * You are responsible for closing the message box when the process is complete.
16063          * @param {String} msg The message box body text
16064          * @param {String} title (optional) The title bar text
16065          * @return {Roo.MessageBox} This message box
16066          */
16067         wait : function(msg, title){
16068             this.show({
16069                 title : title,
16070                 msg : msg,
16071                 buttons: false,
16072                 closable:false,
16073                 progress:true,
16074                 modal:true,
16075                 width:300,
16076                 wait:true
16077             });
16078             waitTimer = Roo.TaskMgr.start({
16079                 run: function(i){
16080                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16081                 },
16082                 interval: 1000
16083             });
16084             return this;
16085         },
16086
16087         /**
16088          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16089          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16090          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16091          * @param {String} title The title bar text
16092          * @param {String} msg The message box body text
16093          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16094          * @param {Object} scope (optional) The scope of the callback function
16095          * @return {Roo.MessageBox} This message box
16096          */
16097         confirm : function(title, msg, fn, scope){
16098             this.show({
16099                 title : title,
16100                 msg : msg,
16101                 buttons: this.YESNO,
16102                 fn: fn,
16103                 scope : scope,
16104                 modal : true
16105             });
16106             return this;
16107         },
16108
16109         /**
16110          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16111          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16112          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16113          * (could also be the top-right close button) and the text that was entered will be passed as the two
16114          * parameters to the callback.
16115          * @param {String} title The title bar text
16116          * @param {String} msg The message box body text
16117          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16118          * @param {Object} scope (optional) The scope of the callback function
16119          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16120          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16121          * @return {Roo.MessageBox} This message box
16122          */
16123         prompt : function(title, msg, fn, scope, multiline){
16124             this.show({
16125                 title : title,
16126                 msg : msg,
16127                 buttons: this.OKCANCEL,
16128                 fn: fn,
16129                 minWidth:250,
16130                 scope : scope,
16131                 prompt:true,
16132                 multiline: multiline,
16133                 modal : true
16134             });
16135             return this;
16136         },
16137
16138         /**
16139          * Button config that displays a single OK button
16140          * @type Object
16141          */
16142         OK : {ok:true},
16143         /**
16144          * Button config that displays Yes and No buttons
16145          * @type Object
16146          */
16147         YESNO : {yes:true, no:true},
16148         /**
16149          * Button config that displays OK and Cancel buttons
16150          * @type Object
16151          */
16152         OKCANCEL : {ok:true, cancel:true},
16153         /**
16154          * Button config that displays Yes, No and Cancel buttons
16155          * @type Object
16156          */
16157         YESNOCANCEL : {yes:true, no:true, cancel:true},
16158
16159         /**
16160          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16161          * @type Number
16162          */
16163         defaultTextHeight : 75,
16164         /**
16165          * The maximum width in pixels of the message box (defaults to 600)
16166          * @type Number
16167          */
16168         maxWidth : 600,
16169         /**
16170          * The minimum width in pixels of the message box (defaults to 100)
16171          * @type Number
16172          */
16173         minWidth : 100,
16174         /**
16175          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16176          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16177          * @type Number
16178          */
16179         minProgressWidth : 250,
16180         /**
16181          * An object containing the default button text strings that can be overriden for localized language support.
16182          * Supported properties are: ok, cancel, yes and no.
16183          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16184          * @type Object
16185          */
16186         buttonText : {
16187             ok : "OK",
16188             cancel : "Cancel",
16189             yes : "Yes",
16190             no : "No"
16191         }
16192     };
16193 }();
16194
16195 /**
16196  * Shorthand for {@link Roo.MessageBox}
16197  */
16198 Roo.Msg = Roo.MessageBox;/*
16199  * Based on:
16200  * Ext JS Library 1.1.1
16201  * Copyright(c) 2006-2007, Ext JS, LLC.
16202  *
16203  * Originally Released Under LGPL - original licence link has changed is not relivant.
16204  *
16205  * Fork - LGPL
16206  * <script type="text/javascript">
16207  */
16208 /**
16209  * @class Roo.QuickTips
16210  * Provides attractive and customizable tooltips for any element.
16211  * @singleton
16212  */
16213 Roo.QuickTips = function(){
16214     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16215     var ce, bd, xy, dd;
16216     var visible = false, disabled = true, inited = false;
16217     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16218     
16219     var onOver = function(e){
16220         if(disabled){
16221             return;
16222         }
16223         var t = e.getTarget();
16224         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16225             return;
16226         }
16227         if(ce && t == ce.el){
16228             clearTimeout(hideProc);
16229             return;
16230         }
16231         if(t && tagEls[t.id]){
16232             tagEls[t.id].el = t;
16233             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16234             return;
16235         }
16236         var ttp, et = Roo.fly(t);
16237         var ns = cfg.namespace;
16238         if(tm.interceptTitles && t.title){
16239             ttp = t.title;
16240             t.qtip = ttp;
16241             t.removeAttribute("title");
16242             e.preventDefault();
16243         }else{
16244             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16245         }
16246         if(ttp){
16247             showProc = show.defer(tm.showDelay, tm, [{
16248                 el: t, 
16249                 text: ttp, 
16250                 width: et.getAttributeNS(ns, cfg.width),
16251                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16252                 title: et.getAttributeNS(ns, cfg.title),
16253                     cls: et.getAttributeNS(ns, cfg.cls)
16254             }]);
16255         }
16256     };
16257     
16258     var onOut = function(e){
16259         clearTimeout(showProc);
16260         var t = e.getTarget();
16261         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16262             hideProc = setTimeout(hide, tm.hideDelay);
16263         }
16264     };
16265     
16266     var onMove = function(e){
16267         if(disabled){
16268             return;
16269         }
16270         xy = e.getXY();
16271         xy[1] += 18;
16272         if(tm.trackMouse && ce){
16273             el.setXY(xy);
16274         }
16275     };
16276     
16277     var onDown = function(e){
16278         clearTimeout(showProc);
16279         clearTimeout(hideProc);
16280         if(!e.within(el)){
16281             if(tm.hideOnClick){
16282                 hide();
16283                 tm.disable();
16284                 tm.enable.defer(100, tm);
16285             }
16286         }
16287     };
16288     
16289     var getPad = function(){
16290         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16291     };
16292
16293     var show = function(o){
16294         if(disabled){
16295             return;
16296         }
16297         clearTimeout(dismissProc);
16298         ce = o;
16299         if(removeCls){ // in case manually hidden
16300             el.removeClass(removeCls);
16301             removeCls = null;
16302         }
16303         if(ce.cls){
16304             el.addClass(ce.cls);
16305             removeCls = ce.cls;
16306         }
16307         if(ce.title){
16308             tipTitle.update(ce.title);
16309             tipTitle.show();
16310         }else{
16311             tipTitle.update('');
16312             tipTitle.hide();
16313         }
16314         el.dom.style.width  = tm.maxWidth+'px';
16315         //tipBody.dom.style.width = '';
16316         tipBodyText.update(o.text);
16317         var p = getPad(), w = ce.width;
16318         if(!w){
16319             var td = tipBodyText.dom;
16320             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16321             if(aw > tm.maxWidth){
16322                 w = tm.maxWidth;
16323             }else if(aw < tm.minWidth){
16324                 w = tm.minWidth;
16325             }else{
16326                 w = aw;
16327             }
16328         }
16329         //tipBody.setWidth(w);
16330         el.setWidth(parseInt(w, 10) + p);
16331         if(ce.autoHide === false){
16332             close.setDisplayed(true);
16333             if(dd){
16334                 dd.unlock();
16335             }
16336         }else{
16337             close.setDisplayed(false);
16338             if(dd){
16339                 dd.lock();
16340             }
16341         }
16342         if(xy){
16343             el.avoidY = xy[1]-18;
16344             el.setXY(xy);
16345         }
16346         if(tm.animate){
16347             el.setOpacity(.1);
16348             el.setStyle("visibility", "visible");
16349             el.fadeIn({callback: afterShow});
16350         }else{
16351             afterShow();
16352         }
16353     };
16354     
16355     var afterShow = function(){
16356         if(ce){
16357             el.show();
16358             esc.enable();
16359             if(tm.autoDismiss && ce.autoHide !== false){
16360                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16361             }
16362         }
16363     };
16364     
16365     var hide = function(noanim){
16366         clearTimeout(dismissProc);
16367         clearTimeout(hideProc);
16368         ce = null;
16369         if(el.isVisible()){
16370             esc.disable();
16371             if(noanim !== true && tm.animate){
16372                 el.fadeOut({callback: afterHide});
16373             }else{
16374                 afterHide();
16375             } 
16376         }
16377     };
16378     
16379     var afterHide = function(){
16380         el.hide();
16381         if(removeCls){
16382             el.removeClass(removeCls);
16383             removeCls = null;
16384         }
16385     };
16386     
16387     return {
16388         /**
16389         * @cfg {Number} minWidth
16390         * The minimum width of the quick tip (defaults to 40)
16391         */
16392        minWidth : 40,
16393         /**
16394         * @cfg {Number} maxWidth
16395         * The maximum width of the quick tip (defaults to 300)
16396         */
16397        maxWidth : 300,
16398         /**
16399         * @cfg {Boolean} interceptTitles
16400         * True to automatically use the element's DOM title value if available (defaults to false)
16401         */
16402        interceptTitles : false,
16403         /**
16404         * @cfg {Boolean} trackMouse
16405         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16406         */
16407        trackMouse : false,
16408         /**
16409         * @cfg {Boolean} hideOnClick
16410         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16411         */
16412        hideOnClick : true,
16413         /**
16414         * @cfg {Number} showDelay
16415         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16416         */
16417        showDelay : 500,
16418         /**
16419         * @cfg {Number} hideDelay
16420         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16421         */
16422        hideDelay : 200,
16423         /**
16424         * @cfg {Boolean} autoHide
16425         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16426         * Used in conjunction with hideDelay.
16427         */
16428        autoHide : true,
16429         /**
16430         * @cfg {Boolean}
16431         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16432         * (defaults to true).  Used in conjunction with autoDismissDelay.
16433         */
16434        autoDismiss : true,
16435         /**
16436         * @cfg {Number}
16437         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16438         */
16439        autoDismissDelay : 5000,
16440        /**
16441         * @cfg {Boolean} animate
16442         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16443         */
16444        animate : false,
16445
16446        /**
16447         * @cfg {String} title
16448         * Title text to display (defaults to '').  This can be any valid HTML markup.
16449         */
16450         title: '',
16451        /**
16452         * @cfg {String} text
16453         * Body text to display (defaults to '').  This can be any valid HTML markup.
16454         */
16455         text : '',
16456        /**
16457         * @cfg {String} cls
16458         * A CSS class to apply to the base quick tip element (defaults to '').
16459         */
16460         cls : '',
16461        /**
16462         * @cfg {Number} width
16463         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16464         * minWidth or maxWidth.
16465         */
16466         width : null,
16467
16468     /**
16469      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16470      * or display QuickTips in a page.
16471      */
16472        init : function(){
16473           tm = Roo.QuickTips;
16474           cfg = tm.tagConfig;
16475           if(!inited){
16476               if(!Roo.isReady){ // allow calling of init() before onReady
16477                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16478                   return;
16479               }
16480               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16481               el.fxDefaults = {stopFx: true};
16482               // maximum custom styling
16483               //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>');
16484               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>');              
16485               tipTitle = el.child('h3');
16486               tipTitle.enableDisplayMode("block");
16487               tipBody = el.child('div.x-tip-bd');
16488               tipBodyText = el.child('div.x-tip-bd-inner');
16489               //bdLeft = el.child('div.x-tip-bd-left');
16490               //bdRight = el.child('div.x-tip-bd-right');
16491               close = el.child('div.x-tip-close');
16492               close.enableDisplayMode("block");
16493               close.on("click", hide);
16494               var d = Roo.get(document);
16495               d.on("mousedown", onDown);
16496               d.on("mouseover", onOver);
16497               d.on("mouseout", onOut);
16498               d.on("mousemove", onMove);
16499               esc = d.addKeyListener(27, hide);
16500               esc.disable();
16501               if(Roo.dd.DD){
16502                   dd = el.initDD("default", null, {
16503                       onDrag : function(){
16504                           el.sync();  
16505                       }
16506                   });
16507                   dd.setHandleElId(tipTitle.id);
16508                   dd.lock();
16509               }
16510               inited = true;
16511           }
16512           this.enable(); 
16513        },
16514
16515     /**
16516      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16517      * are supported:
16518      * <pre>
16519 Property    Type                   Description
16520 ----------  ---------------------  ------------------------------------------------------------------------
16521 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16522      * </ul>
16523      * @param {Object} config The config object
16524      */
16525        register : function(config){
16526            var cs = config instanceof Array ? config : arguments;
16527            for(var i = 0, len = cs.length; i < len; i++) {
16528                var c = cs[i];
16529                var target = c.target;
16530                if(target){
16531                    if(target instanceof Array){
16532                        for(var j = 0, jlen = target.length; j < jlen; j++){
16533                            tagEls[target[j]] = c;
16534                        }
16535                    }else{
16536                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16537                    }
16538                }
16539            }
16540        },
16541
16542     /**
16543      * Removes this quick tip from its element and destroys it.
16544      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16545      */
16546        unregister : function(el){
16547            delete tagEls[Roo.id(el)];
16548        },
16549
16550     /**
16551      * Enable this quick tip.
16552      */
16553        enable : function(){
16554            if(inited && disabled){
16555                locks.pop();
16556                if(locks.length < 1){
16557                    disabled = false;
16558                }
16559            }
16560        },
16561
16562     /**
16563      * Disable this quick tip.
16564      */
16565        disable : function(){
16566           disabled = true;
16567           clearTimeout(showProc);
16568           clearTimeout(hideProc);
16569           clearTimeout(dismissProc);
16570           if(ce){
16571               hide(true);
16572           }
16573           locks.push(1);
16574        },
16575
16576     /**
16577      * Returns true if the quick tip is enabled, else false.
16578      */
16579        isEnabled : function(){
16580             return !disabled;
16581        },
16582
16583         // private
16584        tagConfig : {
16585            namespace : "ext",
16586            attribute : "qtip",
16587            width : "width",
16588            target : "target",
16589            title : "qtitle",
16590            hide : "hide",
16591            cls : "qclass"
16592        }
16593    };
16594 }();
16595
16596 // backwards compat
16597 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16598  * Based on:
16599  * Ext JS Library 1.1.1
16600  * Copyright(c) 2006-2007, Ext JS, LLC.
16601  *
16602  * Originally Released Under LGPL - original licence link has changed is not relivant.
16603  *
16604  * Fork - LGPL
16605  * <script type="text/javascript">
16606  */
16607  
16608
16609 /**
16610  * @class Roo.tree.TreePanel
16611  * @extends Roo.data.Tree
16612
16613  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16614  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16615  * @cfg {Boolean} enableDD true to enable drag and drop
16616  * @cfg {Boolean} enableDrag true to enable just drag
16617  * @cfg {Boolean} enableDrop true to enable just drop
16618  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16619  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16620  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16621  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16622  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16623  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16624  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16625  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16626  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16627  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16628  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16629  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16630  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16631  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16632  * @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>
16633  * @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>
16634  * 
16635  * @constructor
16636  * @param {String/HTMLElement/Element} el The container element
16637  * @param {Object} config
16638  */
16639 Roo.tree.TreePanel = function(el, config){
16640     var root = false;
16641     var loader = false;
16642     if (config.root) {
16643         root = config.root;
16644         delete config.root;
16645     }
16646     if (config.loader) {
16647         loader = config.loader;
16648         delete config.loader;
16649     }
16650     
16651     Roo.apply(this, config);
16652     Roo.tree.TreePanel.superclass.constructor.call(this);
16653     this.el = Roo.get(el);
16654     this.el.addClass('x-tree');
16655     //console.log(root);
16656     if (root) {
16657         this.setRootNode( Roo.factory(root, Roo.tree));
16658     }
16659     if (loader) {
16660         this.loader = Roo.factory(loader, Roo.tree);
16661     }
16662    /**
16663     * Read-only. The id of the container element becomes this TreePanel's id.
16664     */
16665     this.id = this.el.id;
16666     this.addEvents({
16667         /**
16668         * @event beforeload
16669         * Fires before a node is loaded, return false to cancel
16670         * @param {Node} node The node being loaded
16671         */
16672         "beforeload" : true,
16673         /**
16674         * @event load
16675         * Fires when a node is loaded
16676         * @param {Node} node The node that was loaded
16677         */
16678         "load" : true,
16679         /**
16680         * @event textchange
16681         * Fires when the text for a node is changed
16682         * @param {Node} node The node
16683         * @param {String} text The new text
16684         * @param {String} oldText The old text
16685         */
16686         "textchange" : true,
16687         /**
16688         * @event beforeexpand
16689         * Fires before a node is expanded, return false to cancel.
16690         * @param {Node} node The node
16691         * @param {Boolean} deep
16692         * @param {Boolean} anim
16693         */
16694         "beforeexpand" : true,
16695         /**
16696         * @event beforecollapse
16697         * Fires before a node is collapsed, return false to cancel.
16698         * @param {Node} node The node
16699         * @param {Boolean} deep
16700         * @param {Boolean} anim
16701         */
16702         "beforecollapse" : true,
16703         /**
16704         * @event expand
16705         * Fires when a node is expanded
16706         * @param {Node} node The node
16707         */
16708         "expand" : true,
16709         /**
16710         * @event disabledchange
16711         * Fires when the disabled status of a node changes
16712         * @param {Node} node The node
16713         * @param {Boolean} disabled
16714         */
16715         "disabledchange" : true,
16716         /**
16717         * @event collapse
16718         * Fires when a node is collapsed
16719         * @param {Node} node The node
16720         */
16721         "collapse" : true,
16722         /**
16723         * @event beforeclick
16724         * Fires before click processing on a node. Return false to cancel the default action.
16725         * @param {Node} node The node
16726         * @param {Roo.EventObject} e The event object
16727         */
16728         "beforeclick":true,
16729         /**
16730         * @event checkchange
16731         * Fires when a node with a checkbox's checked property changes
16732         * @param {Node} this This node
16733         * @param {Boolean} checked
16734         */
16735         "checkchange":true,
16736         /**
16737         * @event click
16738         * Fires when a node is clicked
16739         * @param {Node} node The node
16740         * @param {Roo.EventObject} e The event object
16741         */
16742         "click":true,
16743         /**
16744         * @event dblclick
16745         * Fires when a node is double clicked
16746         * @param {Node} node The node
16747         * @param {Roo.EventObject} e The event object
16748         */
16749         "dblclick":true,
16750         /**
16751         * @event contextmenu
16752         * Fires when a node is right clicked
16753         * @param {Node} node The node
16754         * @param {Roo.EventObject} e The event object
16755         */
16756         "contextmenu":true,
16757         /**
16758         * @event beforechildrenrendered
16759         * Fires right before the child nodes for a node are rendered
16760         * @param {Node} node The node
16761         */
16762         "beforechildrenrendered":true,
16763         /**
16764         * @event startdrag
16765         * Fires when a node starts being dragged
16766         * @param {Roo.tree.TreePanel} this
16767         * @param {Roo.tree.TreeNode} node
16768         * @param {event} e The raw browser event
16769         */ 
16770        "startdrag" : true,
16771        /**
16772         * @event enddrag
16773         * Fires when a drag operation is complete
16774         * @param {Roo.tree.TreePanel} this
16775         * @param {Roo.tree.TreeNode} node
16776         * @param {event} e The raw browser event
16777         */
16778        "enddrag" : true,
16779        /**
16780         * @event dragdrop
16781         * Fires when a dragged node is dropped on a valid DD target
16782         * @param {Roo.tree.TreePanel} this
16783         * @param {Roo.tree.TreeNode} node
16784         * @param {DD} dd The dd it was dropped on
16785         * @param {event} e The raw browser event
16786         */
16787        "dragdrop" : true,
16788        /**
16789         * @event beforenodedrop
16790         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16791         * passed to handlers has the following properties:<br />
16792         * <ul style="padding:5px;padding-left:16px;">
16793         * <li>tree - The TreePanel</li>
16794         * <li>target - The node being targeted for the drop</li>
16795         * <li>data - The drag data from the drag source</li>
16796         * <li>point - The point of the drop - append, above or below</li>
16797         * <li>source - The drag source</li>
16798         * <li>rawEvent - Raw mouse event</li>
16799         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16800         * to be inserted by setting them on this object.</li>
16801         * <li>cancel - Set this to true to cancel the drop.</li>
16802         * </ul>
16803         * @param {Object} dropEvent
16804         */
16805        "beforenodedrop" : true,
16806        /**
16807         * @event nodedrop
16808         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16809         * passed to handlers has the following properties:<br />
16810         * <ul style="padding:5px;padding-left:16px;">
16811         * <li>tree - The TreePanel</li>
16812         * <li>target - The node being targeted for the drop</li>
16813         * <li>data - The drag data from the drag source</li>
16814         * <li>point - The point of the drop - append, above or below</li>
16815         * <li>source - The drag source</li>
16816         * <li>rawEvent - Raw mouse event</li>
16817         * <li>dropNode - Dropped node(s).</li>
16818         * </ul>
16819         * @param {Object} dropEvent
16820         */
16821        "nodedrop" : true,
16822         /**
16823         * @event nodedragover
16824         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16825         * passed to handlers has the following properties:<br />
16826         * <ul style="padding:5px;padding-left:16px;">
16827         * <li>tree - The TreePanel</li>
16828         * <li>target - The node being targeted for the drop</li>
16829         * <li>data - The drag data from the drag source</li>
16830         * <li>point - The point of the drop - append, above or below</li>
16831         * <li>source - The drag source</li>
16832         * <li>rawEvent - Raw mouse event</li>
16833         * <li>dropNode - Drop node(s) provided by the source.</li>
16834         * <li>cancel - Set this to true to signal drop not allowed.</li>
16835         * </ul>
16836         * @param {Object} dragOverEvent
16837         */
16838        "nodedragover" : true
16839         
16840     });
16841     if(this.singleExpand){
16842        this.on("beforeexpand", this.restrictExpand, this);
16843     }
16844     if (this.editor) {
16845         this.editor.tree = this;
16846         this.editor = Roo.factory(this.editor, Roo.tree);
16847     }
16848     
16849     if (this.selModel) {
16850         this.selModel = Roo.factory(this.selModel, Roo.tree);
16851     }
16852    
16853 };
16854 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16855     rootVisible : true,
16856     animate: Roo.enableFx,
16857     lines : true,
16858     enableDD : false,
16859     hlDrop : Roo.enableFx,
16860   
16861     renderer: false,
16862     
16863     rendererTip: false,
16864     // private
16865     restrictExpand : function(node){
16866         var p = node.parentNode;
16867         if(p){
16868             if(p.expandedChild && p.expandedChild.parentNode == p){
16869                 p.expandedChild.collapse();
16870             }
16871             p.expandedChild = node;
16872         }
16873     },
16874
16875     // private override
16876     setRootNode : function(node){
16877         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16878         if(!this.rootVisible){
16879             node.ui = new Roo.tree.RootTreeNodeUI(node);
16880         }
16881         return node;
16882     },
16883
16884     /**
16885      * Returns the container element for this TreePanel
16886      */
16887     getEl : function(){
16888         return this.el;
16889     },
16890
16891     /**
16892      * Returns the default TreeLoader for this TreePanel
16893      */
16894     getLoader : function(){
16895         return this.loader;
16896     },
16897
16898     /**
16899      * Expand all nodes
16900      */
16901     expandAll : function(){
16902         this.root.expand(true);
16903     },
16904
16905     /**
16906      * Collapse all nodes
16907      */
16908     collapseAll : function(){
16909         this.root.collapse(true);
16910     },
16911
16912     /**
16913      * Returns the selection model used by this TreePanel
16914      */
16915     getSelectionModel : function(){
16916         if(!this.selModel){
16917             this.selModel = new Roo.tree.DefaultSelectionModel();
16918         }
16919         return this.selModel;
16920     },
16921
16922     /**
16923      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16924      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16925      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16926      * @return {Array}
16927      */
16928     getChecked : function(a, startNode){
16929         startNode = startNode || this.root;
16930         var r = [];
16931         var f = function(){
16932             if(this.attributes.checked){
16933                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16934             }
16935         }
16936         startNode.cascade(f);
16937         return r;
16938     },
16939
16940     /**
16941      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16942      * @param {String} path
16943      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16944      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16945      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16946      */
16947     expandPath : function(path, attr, callback){
16948         attr = attr || "id";
16949         var keys = path.split(this.pathSeparator);
16950         var curNode = this.root;
16951         if(curNode.attributes[attr] != keys[1]){ // invalid root
16952             if(callback){
16953                 callback(false, null);
16954             }
16955             return;
16956         }
16957         var index = 1;
16958         var f = function(){
16959             if(++index == keys.length){
16960                 if(callback){
16961                     callback(true, curNode);
16962                 }
16963                 return;
16964             }
16965             var c = curNode.findChild(attr, keys[index]);
16966             if(!c){
16967                 if(callback){
16968                     callback(false, curNode);
16969                 }
16970                 return;
16971             }
16972             curNode = c;
16973             c.expand(false, false, f);
16974         };
16975         curNode.expand(false, false, f);
16976     },
16977
16978     /**
16979      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16980      * @param {String} path
16981      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16982      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16983      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16984      */
16985     selectPath : function(path, attr, callback){
16986         attr = attr || "id";
16987         var keys = path.split(this.pathSeparator);
16988         var v = keys.pop();
16989         if(keys.length > 0){
16990             var f = function(success, node){
16991                 if(success && node){
16992                     var n = node.findChild(attr, v);
16993                     if(n){
16994                         n.select();
16995                         if(callback){
16996                             callback(true, n);
16997                         }
16998                     }else if(callback){
16999                         callback(false, n);
17000                     }
17001                 }else{
17002                     if(callback){
17003                         callback(false, n);
17004                     }
17005                 }
17006             };
17007             this.expandPath(keys.join(this.pathSeparator), attr, f);
17008         }else{
17009             this.root.select();
17010             if(callback){
17011                 callback(true, this.root);
17012             }
17013         }
17014     },
17015
17016     getTreeEl : function(){
17017         return this.el;
17018     },
17019
17020     /**
17021      * Trigger rendering of this TreePanel
17022      */
17023     render : function(){
17024         if (this.innerCt) {
17025             return this; // stop it rendering more than once!!
17026         }
17027         
17028         this.innerCt = this.el.createChild({tag:"ul",
17029                cls:"x-tree-root-ct " +
17030                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17031
17032         if(this.containerScroll){
17033             Roo.dd.ScrollManager.register(this.el);
17034         }
17035         if((this.enableDD || this.enableDrop) && !this.dropZone){
17036            /**
17037             * The dropZone used by this tree if drop is enabled
17038             * @type Roo.tree.TreeDropZone
17039             */
17040              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17041                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17042            });
17043         }
17044         if((this.enableDD || this.enableDrag) && !this.dragZone){
17045            /**
17046             * The dragZone used by this tree if drag is enabled
17047             * @type Roo.tree.TreeDragZone
17048             */
17049             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17050                ddGroup: this.ddGroup || "TreeDD",
17051                scroll: this.ddScroll
17052            });
17053         }
17054         this.getSelectionModel().init(this);
17055         if (!this.root) {
17056             Roo.log("ROOT not set in tree");
17057             return this;
17058         }
17059         this.root.render();
17060         if(!this.rootVisible){
17061             this.root.renderChildren();
17062         }
17063         return this;
17064     }
17065 });/*
17066  * Based on:
17067  * Ext JS Library 1.1.1
17068  * Copyright(c) 2006-2007, Ext JS, LLC.
17069  *
17070  * Originally Released Under LGPL - original licence link has changed is not relivant.
17071  *
17072  * Fork - LGPL
17073  * <script type="text/javascript">
17074  */
17075  
17076
17077 /**
17078  * @class Roo.tree.DefaultSelectionModel
17079  * @extends Roo.util.Observable
17080  * The default single selection for a TreePanel.
17081  * @param {Object} cfg Configuration
17082  */
17083 Roo.tree.DefaultSelectionModel = function(cfg){
17084    this.selNode = null;
17085    
17086    
17087    
17088    this.addEvents({
17089        /**
17090         * @event selectionchange
17091         * Fires when the selected node changes
17092         * @param {DefaultSelectionModel} this
17093         * @param {TreeNode} node the new selection
17094         */
17095        "selectionchange" : true,
17096
17097        /**
17098         * @event beforeselect
17099         * Fires before the selected node changes, return false to cancel the change
17100         * @param {DefaultSelectionModel} this
17101         * @param {TreeNode} node the new selection
17102         * @param {TreeNode} node the old selection
17103         */
17104        "beforeselect" : true
17105    });
17106    
17107     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17108 };
17109
17110 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17111     init : function(tree){
17112         this.tree = tree;
17113         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17114         tree.on("click", this.onNodeClick, this);
17115     },
17116     
17117     onNodeClick : function(node, e){
17118         if (e.ctrlKey && this.selNode == node)  {
17119             this.unselect(node);
17120             return;
17121         }
17122         this.select(node);
17123     },
17124     
17125     /**
17126      * Select a node.
17127      * @param {TreeNode} node The node to select
17128      * @return {TreeNode} The selected node
17129      */
17130     select : function(node){
17131         var last = this.selNode;
17132         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17133             if(last){
17134                 last.ui.onSelectedChange(false);
17135             }
17136             this.selNode = node;
17137             node.ui.onSelectedChange(true);
17138             this.fireEvent("selectionchange", this, node, last);
17139         }
17140         return node;
17141     },
17142     
17143     /**
17144      * Deselect a node.
17145      * @param {TreeNode} node The node to unselect
17146      */
17147     unselect : function(node){
17148         if(this.selNode == node){
17149             this.clearSelections();
17150         }    
17151     },
17152     
17153     /**
17154      * Clear all selections
17155      */
17156     clearSelections : function(){
17157         var n = this.selNode;
17158         if(n){
17159             n.ui.onSelectedChange(false);
17160             this.selNode = null;
17161             this.fireEvent("selectionchange", this, null);
17162         }
17163         return n;
17164     },
17165     
17166     /**
17167      * Get the selected node
17168      * @return {TreeNode} The selected node
17169      */
17170     getSelectedNode : function(){
17171         return this.selNode;    
17172     },
17173     
17174     /**
17175      * Returns true if the node is selected
17176      * @param {TreeNode} node The node to check
17177      * @return {Boolean}
17178      */
17179     isSelected : function(node){
17180         return this.selNode == node;  
17181     },
17182
17183     /**
17184      * Selects the node above the selected node in the tree, intelligently walking the nodes
17185      * @return TreeNode The new selection
17186      */
17187     selectPrevious : function(){
17188         var s = this.selNode || this.lastSelNode;
17189         if(!s){
17190             return null;
17191         }
17192         var ps = s.previousSibling;
17193         if(ps){
17194             if(!ps.isExpanded() || ps.childNodes.length < 1){
17195                 return this.select(ps);
17196             } else{
17197                 var lc = ps.lastChild;
17198                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17199                     lc = lc.lastChild;
17200                 }
17201                 return this.select(lc);
17202             }
17203         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17204             return this.select(s.parentNode);
17205         }
17206         return null;
17207     },
17208
17209     /**
17210      * Selects the node above the selected node in the tree, intelligently walking the nodes
17211      * @return TreeNode The new selection
17212      */
17213     selectNext : function(){
17214         var s = this.selNode || this.lastSelNode;
17215         if(!s){
17216             return null;
17217         }
17218         if(s.firstChild && s.isExpanded()){
17219              return this.select(s.firstChild);
17220          }else if(s.nextSibling){
17221              return this.select(s.nextSibling);
17222          }else if(s.parentNode){
17223             var newS = null;
17224             s.parentNode.bubble(function(){
17225                 if(this.nextSibling){
17226                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17227                     return false;
17228                 }
17229             });
17230             return newS;
17231          }
17232         return null;
17233     },
17234
17235     onKeyDown : function(e){
17236         var s = this.selNode || this.lastSelNode;
17237         // undesirable, but required
17238         var sm = this;
17239         if(!s){
17240             return;
17241         }
17242         var k = e.getKey();
17243         switch(k){
17244              case e.DOWN:
17245                  e.stopEvent();
17246                  this.selectNext();
17247              break;
17248              case e.UP:
17249                  e.stopEvent();
17250                  this.selectPrevious();
17251              break;
17252              case e.RIGHT:
17253                  e.preventDefault();
17254                  if(s.hasChildNodes()){
17255                      if(!s.isExpanded()){
17256                          s.expand();
17257                      }else if(s.firstChild){
17258                          this.select(s.firstChild, e);
17259                      }
17260                  }
17261              break;
17262              case e.LEFT:
17263                  e.preventDefault();
17264                  if(s.hasChildNodes() && s.isExpanded()){
17265                      s.collapse();
17266                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17267                      this.select(s.parentNode, e);
17268                  }
17269              break;
17270         };
17271     }
17272 });
17273
17274 /**
17275  * @class Roo.tree.MultiSelectionModel
17276  * @extends Roo.util.Observable
17277  * Multi selection for a TreePanel.
17278  * @param {Object} cfg Configuration
17279  */
17280 Roo.tree.MultiSelectionModel = function(){
17281    this.selNodes = [];
17282    this.selMap = {};
17283    this.addEvents({
17284        /**
17285         * @event selectionchange
17286         * Fires when the selected nodes change
17287         * @param {MultiSelectionModel} this
17288         * @param {Array} nodes Array of the selected nodes
17289         */
17290        "selectionchange" : true
17291    });
17292    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17293    
17294 };
17295
17296 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17297     init : function(tree){
17298         this.tree = tree;
17299         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17300         tree.on("click", this.onNodeClick, this);
17301     },
17302     
17303     onNodeClick : function(node, e){
17304         this.select(node, e, e.ctrlKey);
17305     },
17306     
17307     /**
17308      * Select a node.
17309      * @param {TreeNode} node The node to select
17310      * @param {EventObject} e (optional) An event associated with the selection
17311      * @param {Boolean} keepExisting True to retain existing selections
17312      * @return {TreeNode} The selected node
17313      */
17314     select : function(node, e, keepExisting){
17315         if(keepExisting !== true){
17316             this.clearSelections(true);
17317         }
17318         if(this.isSelected(node)){
17319             this.lastSelNode = node;
17320             return node;
17321         }
17322         this.selNodes.push(node);
17323         this.selMap[node.id] = node;
17324         this.lastSelNode = node;
17325         node.ui.onSelectedChange(true);
17326         this.fireEvent("selectionchange", this, this.selNodes);
17327         return node;
17328     },
17329     
17330     /**
17331      * Deselect a node.
17332      * @param {TreeNode} node The node to unselect
17333      */
17334     unselect : function(node){
17335         if(this.selMap[node.id]){
17336             node.ui.onSelectedChange(false);
17337             var sn = this.selNodes;
17338             var index = -1;
17339             if(sn.indexOf){
17340                 index = sn.indexOf(node);
17341             }else{
17342                 for(var i = 0, len = sn.length; i < len; i++){
17343                     if(sn[i] == node){
17344                         index = i;
17345                         break;
17346                     }
17347                 }
17348             }
17349             if(index != -1){
17350                 this.selNodes.splice(index, 1);
17351             }
17352             delete this.selMap[node.id];
17353             this.fireEvent("selectionchange", this, this.selNodes);
17354         }
17355     },
17356     
17357     /**
17358      * Clear all selections
17359      */
17360     clearSelections : function(suppressEvent){
17361         var sn = this.selNodes;
17362         if(sn.length > 0){
17363             for(var i = 0, len = sn.length; i < len; i++){
17364                 sn[i].ui.onSelectedChange(false);
17365             }
17366             this.selNodes = [];
17367             this.selMap = {};
17368             if(suppressEvent !== true){
17369                 this.fireEvent("selectionchange", this, this.selNodes);
17370             }
17371         }
17372     },
17373     
17374     /**
17375      * Returns true if the node is selected
17376      * @param {TreeNode} node The node to check
17377      * @return {Boolean}
17378      */
17379     isSelected : function(node){
17380         return this.selMap[node.id] ? true : false;  
17381     },
17382     
17383     /**
17384      * Returns an array of the selected nodes
17385      * @return {Array}
17386      */
17387     getSelectedNodes : function(){
17388         return this.selNodes;    
17389     },
17390
17391     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17392
17393     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17394
17395     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17396 });/*
17397  * Based on:
17398  * Ext JS Library 1.1.1
17399  * Copyright(c) 2006-2007, Ext JS, LLC.
17400  *
17401  * Originally Released Under LGPL - original licence link has changed is not relivant.
17402  *
17403  * Fork - LGPL
17404  * <script type="text/javascript">
17405  */
17406  
17407 /**
17408  * @class Roo.tree.TreeNode
17409  * @extends Roo.data.Node
17410  * @cfg {String} text The text for this node
17411  * @cfg {Boolean} expanded true to start the node expanded
17412  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17413  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17414  * @cfg {Boolean} disabled true to start the node disabled
17415  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17416  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17417  * @cfg {String} cls A css class to be added to the node
17418  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17419  * @cfg {String} href URL of the link used for the node (defaults to #)
17420  * @cfg {String} hrefTarget target frame for the link
17421  * @cfg {String} qtip An Ext QuickTip for the node
17422  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17423  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17424  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17425  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17426  * (defaults to undefined with no checkbox rendered)
17427  * @constructor
17428  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17429  */
17430 Roo.tree.TreeNode = function(attributes){
17431     attributes = attributes || {};
17432     if(typeof attributes == "string"){
17433         attributes = {text: attributes};
17434     }
17435     this.childrenRendered = false;
17436     this.rendered = false;
17437     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17438     this.expanded = attributes.expanded === true;
17439     this.isTarget = attributes.isTarget !== false;
17440     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17441     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17442
17443     /**
17444      * Read-only. The text for this node. To change it use setText().
17445      * @type String
17446      */
17447     this.text = attributes.text;
17448     /**
17449      * True if this node is disabled.
17450      * @type Boolean
17451      */
17452     this.disabled = attributes.disabled === true;
17453
17454     this.addEvents({
17455         /**
17456         * @event textchange
17457         * Fires when the text for this node is changed
17458         * @param {Node} this This node
17459         * @param {String} text The new text
17460         * @param {String} oldText The old text
17461         */
17462         "textchange" : true,
17463         /**
17464         * @event beforeexpand
17465         * Fires before this node is expanded, return false to cancel.
17466         * @param {Node} this This node
17467         * @param {Boolean} deep
17468         * @param {Boolean} anim
17469         */
17470         "beforeexpand" : true,
17471         /**
17472         * @event beforecollapse
17473         * Fires before this node is collapsed, return false to cancel.
17474         * @param {Node} this This node
17475         * @param {Boolean} deep
17476         * @param {Boolean} anim
17477         */
17478         "beforecollapse" : true,
17479         /**
17480         * @event expand
17481         * Fires when this node is expanded
17482         * @param {Node} this This node
17483         */
17484         "expand" : true,
17485         /**
17486         * @event disabledchange
17487         * Fires when the disabled status of this node changes
17488         * @param {Node} this This node
17489         * @param {Boolean} disabled
17490         */
17491         "disabledchange" : true,
17492         /**
17493         * @event collapse
17494         * Fires when this node is collapsed
17495         * @param {Node} this This node
17496         */
17497         "collapse" : true,
17498         /**
17499         * @event beforeclick
17500         * Fires before click processing. Return false to cancel the default action.
17501         * @param {Node} this This node
17502         * @param {Roo.EventObject} e The event object
17503         */
17504         "beforeclick":true,
17505         /**
17506         * @event checkchange
17507         * Fires when a node with a checkbox's checked property changes
17508         * @param {Node} this This node
17509         * @param {Boolean} checked
17510         */
17511         "checkchange":true,
17512         /**
17513         * @event click
17514         * Fires when this node is clicked
17515         * @param {Node} this This node
17516         * @param {Roo.EventObject} e The event object
17517         */
17518         "click":true,
17519         /**
17520         * @event dblclick
17521         * Fires when this node is double clicked
17522         * @param {Node} this This node
17523         * @param {Roo.EventObject} e The event object
17524         */
17525         "dblclick":true,
17526         /**
17527         * @event contextmenu
17528         * Fires when this node is right clicked
17529         * @param {Node} this This node
17530         * @param {Roo.EventObject} e The event object
17531         */
17532         "contextmenu":true,
17533         /**
17534         * @event beforechildrenrendered
17535         * Fires right before the child nodes for this node are rendered
17536         * @param {Node} this This node
17537         */
17538         "beforechildrenrendered":true
17539     });
17540
17541     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17542
17543     /**
17544      * Read-only. The UI for this node
17545      * @type TreeNodeUI
17546      */
17547     this.ui = new uiClass(this);
17548     
17549     // finally support items[]
17550     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17551         return;
17552     }
17553     
17554     
17555     Roo.each(this.attributes.items, function(c) {
17556         this.appendChild(Roo.factory(c,Roo.Tree));
17557     }, this);
17558     delete this.attributes.items;
17559     
17560     
17561     
17562 };
17563 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17564     preventHScroll: true,
17565     /**
17566      * Returns true if this node is expanded
17567      * @return {Boolean}
17568      */
17569     isExpanded : function(){
17570         return this.expanded;
17571     },
17572
17573     /**
17574      * Returns the UI object for this node
17575      * @return {TreeNodeUI}
17576      */
17577     getUI : function(){
17578         return this.ui;
17579     },
17580
17581     // private override
17582     setFirstChild : function(node){
17583         var of = this.firstChild;
17584         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17585         if(this.childrenRendered && of && node != of){
17586             of.renderIndent(true, true);
17587         }
17588         if(this.rendered){
17589             this.renderIndent(true, true);
17590         }
17591     },
17592
17593     // private override
17594     setLastChild : function(node){
17595         var ol = this.lastChild;
17596         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17597         if(this.childrenRendered && ol && node != ol){
17598             ol.renderIndent(true, true);
17599         }
17600         if(this.rendered){
17601             this.renderIndent(true, true);
17602         }
17603     },
17604
17605     // these methods are overridden to provide lazy rendering support
17606     // private override
17607     appendChild : function()
17608     {
17609         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17610         if(node && this.childrenRendered){
17611             node.render();
17612         }
17613         this.ui.updateExpandIcon();
17614         return node;
17615     },
17616
17617     // private override
17618     removeChild : function(node){
17619         this.ownerTree.getSelectionModel().unselect(node);
17620         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17621         // if it's been rendered remove dom node
17622         if(this.childrenRendered){
17623             node.ui.remove();
17624         }
17625         if(this.childNodes.length < 1){
17626             this.collapse(false, false);
17627         }else{
17628             this.ui.updateExpandIcon();
17629         }
17630         if(!this.firstChild) {
17631             this.childrenRendered = false;
17632         }
17633         return node;
17634     },
17635
17636     // private override
17637     insertBefore : function(node, refNode){
17638         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17639         if(newNode && refNode && this.childrenRendered){
17640             node.render();
17641         }
17642         this.ui.updateExpandIcon();
17643         return newNode;
17644     },
17645
17646     /**
17647      * Sets the text for this node
17648      * @param {String} text
17649      */
17650     setText : function(text){
17651         var oldText = this.text;
17652         this.text = text;
17653         this.attributes.text = text;
17654         if(this.rendered){ // event without subscribing
17655             this.ui.onTextChange(this, text, oldText);
17656         }
17657         this.fireEvent("textchange", this, text, oldText);
17658     },
17659
17660     /**
17661      * Triggers selection of this node
17662      */
17663     select : function(){
17664         this.getOwnerTree().getSelectionModel().select(this);
17665     },
17666
17667     /**
17668      * Triggers deselection of this node
17669      */
17670     unselect : function(){
17671         this.getOwnerTree().getSelectionModel().unselect(this);
17672     },
17673
17674     /**
17675      * Returns true if this node is selected
17676      * @return {Boolean}
17677      */
17678     isSelected : function(){
17679         return this.getOwnerTree().getSelectionModel().isSelected(this);
17680     },
17681
17682     /**
17683      * Expand this node.
17684      * @param {Boolean} deep (optional) True to expand all children as well
17685      * @param {Boolean} anim (optional) false to cancel the default animation
17686      * @param {Function} callback (optional) A callback to be called when
17687      * expanding this node completes (does not wait for deep expand to complete).
17688      * Called with 1 parameter, this node.
17689      */
17690     expand : function(deep, anim, callback){
17691         if(!this.expanded){
17692             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17693                 return;
17694             }
17695             if(!this.childrenRendered){
17696                 this.renderChildren();
17697             }
17698             this.expanded = true;
17699             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17700                 this.ui.animExpand(function(){
17701                     this.fireEvent("expand", this);
17702                     if(typeof callback == "function"){
17703                         callback(this);
17704                     }
17705                     if(deep === true){
17706                         this.expandChildNodes(true);
17707                     }
17708                 }.createDelegate(this));
17709                 return;
17710             }else{
17711                 this.ui.expand();
17712                 this.fireEvent("expand", this);
17713                 if(typeof callback == "function"){
17714                     callback(this);
17715                 }
17716             }
17717         }else{
17718            if(typeof callback == "function"){
17719                callback(this);
17720            }
17721         }
17722         if(deep === true){
17723             this.expandChildNodes(true);
17724         }
17725     },
17726
17727     isHiddenRoot : function(){
17728         return this.isRoot && !this.getOwnerTree().rootVisible;
17729     },
17730
17731     /**
17732      * Collapse this node.
17733      * @param {Boolean} deep (optional) True to collapse all children as well
17734      * @param {Boolean} anim (optional) false to cancel the default animation
17735      */
17736     collapse : function(deep, anim){
17737         if(this.expanded && !this.isHiddenRoot()){
17738             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17739                 return;
17740             }
17741             this.expanded = false;
17742             if((this.getOwnerTree().animate && anim !== false) || anim){
17743                 this.ui.animCollapse(function(){
17744                     this.fireEvent("collapse", this);
17745                     if(deep === true){
17746                         this.collapseChildNodes(true);
17747                     }
17748                 }.createDelegate(this));
17749                 return;
17750             }else{
17751                 this.ui.collapse();
17752                 this.fireEvent("collapse", this);
17753             }
17754         }
17755         if(deep === true){
17756             var cs = this.childNodes;
17757             for(var i = 0, len = cs.length; i < len; i++) {
17758                 cs[i].collapse(true, false);
17759             }
17760         }
17761     },
17762
17763     // private
17764     delayedExpand : function(delay){
17765         if(!this.expandProcId){
17766             this.expandProcId = this.expand.defer(delay, this);
17767         }
17768     },
17769
17770     // private
17771     cancelExpand : function(){
17772         if(this.expandProcId){
17773             clearTimeout(this.expandProcId);
17774         }
17775         this.expandProcId = false;
17776     },
17777
17778     /**
17779      * Toggles expanded/collapsed state of the node
17780      */
17781     toggle : function(){
17782         if(this.expanded){
17783             this.collapse();
17784         }else{
17785             this.expand();
17786         }
17787     },
17788
17789     /**
17790      * Ensures all parent nodes are expanded
17791      */
17792     ensureVisible : function(callback){
17793         var tree = this.getOwnerTree();
17794         tree.expandPath(this.parentNode.getPath(), false, function(){
17795             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17796             Roo.callback(callback);
17797         }.createDelegate(this));
17798     },
17799
17800     /**
17801      * Expand all child nodes
17802      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17803      */
17804     expandChildNodes : function(deep){
17805         var cs = this.childNodes;
17806         for(var i = 0, len = cs.length; i < len; i++) {
17807                 cs[i].expand(deep);
17808         }
17809     },
17810
17811     /**
17812      * Collapse all child nodes
17813      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17814      */
17815     collapseChildNodes : function(deep){
17816         var cs = this.childNodes;
17817         for(var i = 0, len = cs.length; i < len; i++) {
17818                 cs[i].collapse(deep);
17819         }
17820     },
17821
17822     /**
17823      * Disables this node
17824      */
17825     disable : function(){
17826         this.disabled = true;
17827         this.unselect();
17828         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17829             this.ui.onDisableChange(this, true);
17830         }
17831         this.fireEvent("disabledchange", this, true);
17832     },
17833
17834     /**
17835      * Enables this node
17836      */
17837     enable : function(){
17838         this.disabled = false;
17839         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17840             this.ui.onDisableChange(this, false);
17841         }
17842         this.fireEvent("disabledchange", this, false);
17843     },
17844
17845     // private
17846     renderChildren : function(suppressEvent){
17847         if(suppressEvent !== false){
17848             this.fireEvent("beforechildrenrendered", this);
17849         }
17850         var cs = this.childNodes;
17851         for(var i = 0, len = cs.length; i < len; i++){
17852             cs[i].render(true);
17853         }
17854         this.childrenRendered = true;
17855     },
17856
17857     // private
17858     sort : function(fn, scope){
17859         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17860         if(this.childrenRendered){
17861             var cs = this.childNodes;
17862             for(var i = 0, len = cs.length; i < len; i++){
17863                 cs[i].render(true);
17864             }
17865         }
17866     },
17867
17868     // private
17869     render : function(bulkRender){
17870         this.ui.render(bulkRender);
17871         if(!this.rendered){
17872             this.rendered = true;
17873             if(this.expanded){
17874                 this.expanded = false;
17875                 this.expand(false, false);
17876             }
17877         }
17878     },
17879
17880     // private
17881     renderIndent : function(deep, refresh){
17882         if(refresh){
17883             this.ui.childIndent = null;
17884         }
17885         this.ui.renderIndent();
17886         if(deep === true && this.childrenRendered){
17887             var cs = this.childNodes;
17888             for(var i = 0, len = cs.length; i < len; i++){
17889                 cs[i].renderIndent(true, refresh);
17890             }
17891         }
17892     }
17893 });/*
17894  * Based on:
17895  * Ext JS Library 1.1.1
17896  * Copyright(c) 2006-2007, Ext JS, LLC.
17897  *
17898  * Originally Released Under LGPL - original licence link has changed is not relivant.
17899  *
17900  * Fork - LGPL
17901  * <script type="text/javascript">
17902  */
17903  
17904 /**
17905  * @class Roo.tree.AsyncTreeNode
17906  * @extends Roo.tree.TreeNode
17907  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17908  * @constructor
17909  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17910  */
17911  Roo.tree.AsyncTreeNode = function(config){
17912     this.loaded = false;
17913     this.loading = false;
17914     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17915     /**
17916     * @event beforeload
17917     * Fires before this node is loaded, return false to cancel
17918     * @param {Node} this This node
17919     */
17920     this.addEvents({'beforeload':true, 'load': true});
17921     /**
17922     * @event load
17923     * Fires when this node is loaded
17924     * @param {Node} this This node
17925     */
17926     /**
17927      * The loader used by this node (defaults to using the tree's defined loader)
17928      * @type TreeLoader
17929      * @property loader
17930      */
17931 };
17932 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17933     expand : function(deep, anim, callback){
17934         if(this.loading){ // if an async load is already running, waiting til it's done
17935             var timer;
17936             var f = function(){
17937                 if(!this.loading){ // done loading
17938                     clearInterval(timer);
17939                     this.expand(deep, anim, callback);
17940                 }
17941             }.createDelegate(this);
17942             timer = setInterval(f, 200);
17943             return;
17944         }
17945         if(!this.loaded){
17946             if(this.fireEvent("beforeload", this) === false){
17947                 return;
17948             }
17949             this.loading = true;
17950             this.ui.beforeLoad(this);
17951             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17952             if(loader){
17953                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17954                 return;
17955             }
17956         }
17957         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17958     },
17959     
17960     /**
17961      * Returns true if this node is currently loading
17962      * @return {Boolean}
17963      */
17964     isLoading : function(){
17965         return this.loading;  
17966     },
17967     
17968     loadComplete : function(deep, anim, callback){
17969         this.loading = false;
17970         this.loaded = true;
17971         this.ui.afterLoad(this);
17972         this.fireEvent("load", this);
17973         this.expand(deep, anim, callback);
17974     },
17975     
17976     /**
17977      * Returns true if this node has been loaded
17978      * @return {Boolean}
17979      */
17980     isLoaded : function(){
17981         return this.loaded;
17982     },
17983     
17984     hasChildNodes : function(){
17985         if(!this.isLeaf() && !this.loaded){
17986             return true;
17987         }else{
17988             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17989         }
17990     },
17991
17992     /**
17993      * Trigger a reload for this node
17994      * @param {Function} callback
17995      */
17996     reload : function(callback){
17997         this.collapse(false, false);
17998         while(this.firstChild){
17999             this.removeChild(this.firstChild);
18000         }
18001         this.childrenRendered = false;
18002         this.loaded = false;
18003         if(this.isHiddenRoot()){
18004             this.expanded = false;
18005         }
18006         this.expand(false, false, callback);
18007     }
18008 });/*
18009  * Based on:
18010  * Ext JS Library 1.1.1
18011  * Copyright(c) 2006-2007, Ext JS, LLC.
18012  *
18013  * Originally Released Under LGPL - original licence link has changed is not relivant.
18014  *
18015  * Fork - LGPL
18016  * <script type="text/javascript">
18017  */
18018  
18019 /**
18020  * @class Roo.tree.TreeNodeUI
18021  * @constructor
18022  * @param {Object} node The node to render
18023  * The TreeNode UI implementation is separate from the
18024  * tree implementation. Unless you are customizing the tree UI,
18025  * you should never have to use this directly.
18026  */
18027 Roo.tree.TreeNodeUI = function(node){
18028     this.node = node;
18029     this.rendered = false;
18030     this.animating = false;
18031     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18032 };
18033
18034 Roo.tree.TreeNodeUI.prototype = {
18035     removeChild : function(node){
18036         if(this.rendered){
18037             this.ctNode.removeChild(node.ui.getEl());
18038         }
18039     },
18040
18041     beforeLoad : function(){
18042          this.addClass("x-tree-node-loading");
18043     },
18044
18045     afterLoad : function(){
18046          this.removeClass("x-tree-node-loading");
18047     },
18048
18049     onTextChange : function(node, text, oldText){
18050         if(this.rendered){
18051             this.textNode.innerHTML = text;
18052         }
18053     },
18054
18055     onDisableChange : function(node, state){
18056         this.disabled = state;
18057         if(state){
18058             this.addClass("x-tree-node-disabled");
18059         }else{
18060             this.removeClass("x-tree-node-disabled");
18061         }
18062     },
18063
18064     onSelectedChange : function(state){
18065         if(state){
18066             this.focus();
18067             this.addClass("x-tree-selected");
18068         }else{
18069             //this.blur();
18070             this.removeClass("x-tree-selected");
18071         }
18072     },
18073
18074     onMove : function(tree, node, oldParent, newParent, index, refNode){
18075         this.childIndent = null;
18076         if(this.rendered){
18077             var targetNode = newParent.ui.getContainer();
18078             if(!targetNode){//target not rendered
18079                 this.holder = document.createElement("div");
18080                 this.holder.appendChild(this.wrap);
18081                 return;
18082             }
18083             var insertBefore = refNode ? refNode.ui.getEl() : null;
18084             if(insertBefore){
18085                 targetNode.insertBefore(this.wrap, insertBefore);
18086             }else{
18087                 targetNode.appendChild(this.wrap);
18088             }
18089             this.node.renderIndent(true);
18090         }
18091     },
18092
18093     addClass : function(cls){
18094         if(this.elNode){
18095             Roo.fly(this.elNode).addClass(cls);
18096         }
18097     },
18098
18099     removeClass : function(cls){
18100         if(this.elNode){
18101             Roo.fly(this.elNode).removeClass(cls);
18102         }
18103     },
18104
18105     remove : function(){
18106         if(this.rendered){
18107             this.holder = document.createElement("div");
18108             this.holder.appendChild(this.wrap);
18109         }
18110     },
18111
18112     fireEvent : function(){
18113         return this.node.fireEvent.apply(this.node, arguments);
18114     },
18115
18116     initEvents : function(){
18117         this.node.on("move", this.onMove, this);
18118         var E = Roo.EventManager;
18119         var a = this.anchor;
18120
18121         var el = Roo.fly(a, '_treeui');
18122
18123         if(Roo.isOpera){ // opera render bug ignores the CSS
18124             el.setStyle("text-decoration", "none");
18125         }
18126
18127         el.on("click", this.onClick, this);
18128         el.on("dblclick", this.onDblClick, this);
18129
18130         if(this.checkbox){
18131             Roo.EventManager.on(this.checkbox,
18132                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18133         }
18134
18135         el.on("contextmenu", this.onContextMenu, this);
18136
18137         var icon = Roo.fly(this.iconNode);
18138         icon.on("click", this.onClick, this);
18139         icon.on("dblclick", this.onDblClick, this);
18140         icon.on("contextmenu", this.onContextMenu, this);
18141         E.on(this.ecNode, "click", this.ecClick, this, true);
18142
18143         if(this.node.disabled){
18144             this.addClass("x-tree-node-disabled");
18145         }
18146         if(this.node.hidden){
18147             this.addClass("x-tree-node-disabled");
18148         }
18149         var ot = this.node.getOwnerTree();
18150         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18151         if(dd && (!this.node.isRoot || ot.rootVisible)){
18152             Roo.dd.Registry.register(this.elNode, {
18153                 node: this.node,
18154                 handles: this.getDDHandles(),
18155                 isHandle: false
18156             });
18157         }
18158     },
18159
18160     getDDHandles : function(){
18161         return [this.iconNode, this.textNode];
18162     },
18163
18164     hide : function(){
18165         if(this.rendered){
18166             this.wrap.style.display = "none";
18167         }
18168     },
18169
18170     show : function(){
18171         if(this.rendered){
18172             this.wrap.style.display = "";
18173         }
18174     },
18175
18176     onContextMenu : function(e){
18177         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18178             e.preventDefault();
18179             this.focus();
18180             this.fireEvent("contextmenu", this.node, e);
18181         }
18182     },
18183
18184     onClick : function(e){
18185         if(this.dropping){
18186             e.stopEvent();
18187             return;
18188         }
18189         if(this.fireEvent("beforeclick", this.node, e) !== false){
18190             if(!this.disabled && this.node.attributes.href){
18191                 this.fireEvent("click", this.node, e);
18192                 return;
18193             }
18194             e.preventDefault();
18195             if(this.disabled){
18196                 return;
18197             }
18198
18199             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18200                 this.node.toggle();
18201             }
18202
18203             this.fireEvent("click", this.node, e);
18204         }else{
18205             e.stopEvent();
18206         }
18207     },
18208
18209     onDblClick : function(e){
18210         e.preventDefault();
18211         if(this.disabled){
18212             return;
18213         }
18214         if(this.checkbox){
18215             this.toggleCheck();
18216         }
18217         if(!this.animating && this.node.hasChildNodes()){
18218             this.node.toggle();
18219         }
18220         this.fireEvent("dblclick", this.node, e);
18221     },
18222
18223     onCheckChange : function(){
18224         var checked = this.checkbox.checked;
18225         this.node.attributes.checked = checked;
18226         this.fireEvent('checkchange', this.node, checked);
18227     },
18228
18229     ecClick : function(e){
18230         if(!this.animating && this.node.hasChildNodes()){
18231             this.node.toggle();
18232         }
18233     },
18234
18235     startDrop : function(){
18236         this.dropping = true;
18237     },
18238
18239     // delayed drop so the click event doesn't get fired on a drop
18240     endDrop : function(){
18241        setTimeout(function(){
18242            this.dropping = false;
18243        }.createDelegate(this), 50);
18244     },
18245
18246     expand : function(){
18247         this.updateExpandIcon();
18248         this.ctNode.style.display = "";
18249     },
18250
18251     focus : function(){
18252         if(!this.node.preventHScroll){
18253             try{this.anchor.focus();
18254             }catch(e){}
18255         }else if(!Roo.isIE){
18256             try{
18257                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18258                 var l = noscroll.scrollLeft;
18259                 this.anchor.focus();
18260                 noscroll.scrollLeft = l;
18261             }catch(e){}
18262         }
18263     },
18264
18265     toggleCheck : function(value){
18266         var cb = this.checkbox;
18267         if(cb){
18268             cb.checked = (value === undefined ? !cb.checked : value);
18269         }
18270     },
18271
18272     blur : function(){
18273         try{
18274             this.anchor.blur();
18275         }catch(e){}
18276     },
18277
18278     animExpand : function(callback){
18279         var ct = Roo.get(this.ctNode);
18280         ct.stopFx();
18281         if(!this.node.hasChildNodes()){
18282             this.updateExpandIcon();
18283             this.ctNode.style.display = "";
18284             Roo.callback(callback);
18285             return;
18286         }
18287         this.animating = true;
18288         this.updateExpandIcon();
18289
18290         ct.slideIn('t', {
18291            callback : function(){
18292                this.animating = false;
18293                Roo.callback(callback);
18294             },
18295             scope: this,
18296             duration: this.node.ownerTree.duration || .25
18297         });
18298     },
18299
18300     highlight : function(){
18301         var tree = this.node.getOwnerTree();
18302         Roo.fly(this.wrap).highlight(
18303             tree.hlColor || "C3DAF9",
18304             {endColor: tree.hlBaseColor}
18305         );
18306     },
18307
18308     collapse : function(){
18309         this.updateExpandIcon();
18310         this.ctNode.style.display = "none";
18311     },
18312
18313     animCollapse : function(callback){
18314         var ct = Roo.get(this.ctNode);
18315         ct.enableDisplayMode('block');
18316         ct.stopFx();
18317
18318         this.animating = true;
18319         this.updateExpandIcon();
18320
18321         ct.slideOut('t', {
18322             callback : function(){
18323                this.animating = false;
18324                Roo.callback(callback);
18325             },
18326             scope: this,
18327             duration: this.node.ownerTree.duration || .25
18328         });
18329     },
18330
18331     getContainer : function(){
18332         return this.ctNode;
18333     },
18334
18335     getEl : function(){
18336         return this.wrap;
18337     },
18338
18339     appendDDGhost : function(ghostNode){
18340         ghostNode.appendChild(this.elNode.cloneNode(true));
18341     },
18342
18343     getDDRepairXY : function(){
18344         return Roo.lib.Dom.getXY(this.iconNode);
18345     },
18346
18347     onRender : function(){
18348         this.render();
18349     },
18350
18351     render : function(bulkRender){
18352         var n = this.node, a = n.attributes;
18353         var targetNode = n.parentNode ?
18354               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18355
18356         if(!this.rendered){
18357             this.rendered = true;
18358
18359             this.renderElements(n, a, targetNode, bulkRender);
18360
18361             if(a.qtip){
18362                if(this.textNode.setAttributeNS){
18363                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18364                    if(a.qtipTitle){
18365                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18366                    }
18367                }else{
18368                    this.textNode.setAttribute("ext:qtip", a.qtip);
18369                    if(a.qtipTitle){
18370                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18371                    }
18372                }
18373             }else if(a.qtipCfg){
18374                 a.qtipCfg.target = Roo.id(this.textNode);
18375                 Roo.QuickTips.register(a.qtipCfg);
18376             }
18377             this.initEvents();
18378             if(!this.node.expanded){
18379                 this.updateExpandIcon();
18380             }
18381         }else{
18382             if(bulkRender === true) {
18383                 targetNode.appendChild(this.wrap);
18384             }
18385         }
18386     },
18387
18388     renderElements : function(n, a, targetNode, bulkRender)
18389     {
18390         // add some indent caching, this helps performance when rendering a large tree
18391         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18392         var t = n.getOwnerTree();
18393         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18394         if (typeof(n.attributes.html) != 'undefined') {
18395             txt = n.attributes.html;
18396         }
18397         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18398         var cb = typeof a.checked == 'boolean';
18399         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18400         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18401             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18402             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18403             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18404             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18405             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18406              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18407                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18408             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18409             "</li>"];
18410
18411         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18412             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18413                                 n.nextSibling.ui.getEl(), buf.join(""));
18414         }else{
18415             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18416         }
18417
18418         this.elNode = this.wrap.childNodes[0];
18419         this.ctNode = this.wrap.childNodes[1];
18420         var cs = this.elNode.childNodes;
18421         this.indentNode = cs[0];
18422         this.ecNode = cs[1];
18423         this.iconNode = cs[2];
18424         var index = 3;
18425         if(cb){
18426             this.checkbox = cs[3];
18427             index++;
18428         }
18429         this.anchor = cs[index];
18430         this.textNode = cs[index].firstChild;
18431     },
18432
18433     getAnchor : function(){
18434         return this.anchor;
18435     },
18436
18437     getTextEl : function(){
18438         return this.textNode;
18439     },
18440
18441     getIconEl : function(){
18442         return this.iconNode;
18443     },
18444
18445     isChecked : function(){
18446         return this.checkbox ? this.checkbox.checked : false;
18447     },
18448
18449     updateExpandIcon : function(){
18450         if(this.rendered){
18451             var n = this.node, c1, c2;
18452             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18453             var hasChild = n.hasChildNodes();
18454             if(hasChild){
18455                 if(n.expanded){
18456                     cls += "-minus";
18457                     c1 = "x-tree-node-collapsed";
18458                     c2 = "x-tree-node-expanded";
18459                 }else{
18460                     cls += "-plus";
18461                     c1 = "x-tree-node-expanded";
18462                     c2 = "x-tree-node-collapsed";
18463                 }
18464                 if(this.wasLeaf){
18465                     this.removeClass("x-tree-node-leaf");
18466                     this.wasLeaf = false;
18467                 }
18468                 if(this.c1 != c1 || this.c2 != c2){
18469                     Roo.fly(this.elNode).replaceClass(c1, c2);
18470                     this.c1 = c1; this.c2 = c2;
18471                 }
18472             }else{
18473                 // this changes non-leafs into leafs if they have no children.
18474                 // it's not very rational behaviour..
18475                 
18476                 if(!this.wasLeaf && this.node.leaf){
18477                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18478                     delete this.c1;
18479                     delete this.c2;
18480                     this.wasLeaf = true;
18481                 }
18482             }
18483             var ecc = "x-tree-ec-icon "+cls;
18484             if(this.ecc != ecc){
18485                 this.ecNode.className = ecc;
18486                 this.ecc = ecc;
18487             }
18488         }
18489     },
18490
18491     getChildIndent : function(){
18492         if(!this.childIndent){
18493             var buf = [];
18494             var p = this.node;
18495             while(p){
18496                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18497                     if(!p.isLast()) {
18498                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18499                     } else {
18500                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18501                     }
18502                 }
18503                 p = p.parentNode;
18504             }
18505             this.childIndent = buf.join("");
18506         }
18507         return this.childIndent;
18508     },
18509
18510     renderIndent : function(){
18511         if(this.rendered){
18512             var indent = "";
18513             var p = this.node.parentNode;
18514             if(p){
18515                 indent = p.ui.getChildIndent();
18516             }
18517             if(this.indentMarkup != indent){ // don't rerender if not required
18518                 this.indentNode.innerHTML = indent;
18519                 this.indentMarkup = indent;
18520             }
18521             this.updateExpandIcon();
18522         }
18523     }
18524 };
18525
18526 Roo.tree.RootTreeNodeUI = function(){
18527     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18528 };
18529 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18530     render : function(){
18531         if(!this.rendered){
18532             var targetNode = this.node.ownerTree.innerCt.dom;
18533             this.node.expanded = true;
18534             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18535             this.wrap = this.ctNode = targetNode.firstChild;
18536         }
18537     },
18538     collapse : function(){
18539     },
18540     expand : function(){
18541     }
18542 });/*
18543  * Based on:
18544  * Ext JS Library 1.1.1
18545  * Copyright(c) 2006-2007, Ext JS, LLC.
18546  *
18547  * Originally Released Under LGPL - original licence link has changed is not relivant.
18548  *
18549  * Fork - LGPL
18550  * <script type="text/javascript">
18551  */
18552 /**
18553  * @class Roo.tree.TreeLoader
18554  * @extends Roo.util.Observable
18555  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18556  * nodes from a specified URL. The response must be a javascript Array definition
18557  * who's elements are node definition objects. eg:
18558  * <pre><code>
18559 {  success : true,
18560    data :      [
18561    
18562     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18563     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18564     ]
18565 }
18566
18567
18568 </code></pre>
18569  * <br><br>
18570  * The old style respose with just an array is still supported, but not recommended.
18571  * <br><br>
18572  *
18573  * A server request is sent, and child nodes are loaded only when a node is expanded.
18574  * The loading node's id is passed to the server under the parameter name "node" to
18575  * enable the server to produce the correct child nodes.
18576  * <br><br>
18577  * To pass extra parameters, an event handler may be attached to the "beforeload"
18578  * event, and the parameters specified in the TreeLoader's baseParams property:
18579  * <pre><code>
18580     myTreeLoader.on("beforeload", function(treeLoader, node) {
18581         this.baseParams.category = node.attributes.category;
18582     }, this);
18583 </code></pre><
18584  * This would pass an HTTP parameter called "category" to the server containing
18585  * the value of the Node's "category" attribute.
18586  * @constructor
18587  * Creates a new Treeloader.
18588  * @param {Object} config A config object containing config properties.
18589  */
18590 Roo.tree.TreeLoader = function(config){
18591     this.baseParams = {};
18592     this.requestMethod = "POST";
18593     Roo.apply(this, config);
18594
18595     this.addEvents({
18596     
18597         /**
18598          * @event beforeload
18599          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18600          * @param {Object} This TreeLoader object.
18601          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18602          * @param {Object} callback The callback function specified in the {@link #load} call.
18603          */
18604         beforeload : true,
18605         /**
18606          * @event load
18607          * Fires when the node has been successfuly loaded.
18608          * @param {Object} This TreeLoader object.
18609          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18610          * @param {Object} response The response object containing the data from the server.
18611          */
18612         load : true,
18613         /**
18614          * @event loadexception
18615          * Fires if the network request failed.
18616          * @param {Object} This TreeLoader object.
18617          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18618          * @param {Object} response The response object containing the data from the server.
18619          */
18620         loadexception : true,
18621         /**
18622          * @event create
18623          * Fires before a node is created, enabling you to return custom Node types 
18624          * @param {Object} This TreeLoader object.
18625          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18626          */
18627         create : true
18628     });
18629
18630     Roo.tree.TreeLoader.superclass.constructor.call(this);
18631 };
18632
18633 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18634     /**
18635     * @cfg {String} dataUrl The URL from which to request a Json string which
18636     * specifies an array of node definition object representing the child nodes
18637     * to be loaded.
18638     */
18639     /**
18640     * @cfg {String} requestMethod either GET or POST
18641     * defaults to POST (due to BC)
18642     * to be loaded.
18643     */
18644     /**
18645     * @cfg {Object} baseParams (optional) An object containing properties which
18646     * specify HTTP parameters to be passed to each request for child nodes.
18647     */
18648     /**
18649     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18650     * created by this loader. If the attributes sent by the server have an attribute in this object,
18651     * they take priority.
18652     */
18653     /**
18654     * @cfg {Object} uiProviders (optional) An object containing properties which
18655     * 
18656     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18657     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18658     * <i>uiProvider</i> attribute of a returned child node is a string rather
18659     * than a reference to a TreeNodeUI implementation, this that string value
18660     * is used as a property name in the uiProviders object. You can define the provider named
18661     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18662     */
18663     uiProviders : {},
18664
18665     /**
18666     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18667     * child nodes before loading.
18668     */
18669     clearOnLoad : true,
18670
18671     /**
18672     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18673     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18674     * Grid query { data : [ .....] }
18675     */
18676     
18677     root : false,
18678      /**
18679     * @cfg {String} queryParam (optional) 
18680     * Name of the query as it will be passed on the querystring (defaults to 'node')
18681     * eg. the request will be ?node=[id]
18682     */
18683     
18684     
18685     queryParam: false,
18686     
18687     /**
18688      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18689      * This is called automatically when a node is expanded, but may be used to reload
18690      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18691      * @param {Roo.tree.TreeNode} node
18692      * @param {Function} callback
18693      */
18694     load : function(node, callback){
18695         if(this.clearOnLoad){
18696             while(node.firstChild){
18697                 node.removeChild(node.firstChild);
18698             }
18699         }
18700         if(node.attributes.children){ // preloaded json children
18701             var cs = node.attributes.children;
18702             for(var i = 0, len = cs.length; i < len; i++){
18703                 node.appendChild(this.createNode(cs[i]));
18704             }
18705             if(typeof callback == "function"){
18706                 callback();
18707             }
18708         }else if(this.dataUrl){
18709             this.requestData(node, callback);
18710         }
18711     },
18712
18713     getParams: function(node){
18714         var buf = [], bp = this.baseParams;
18715         for(var key in bp){
18716             if(typeof bp[key] != "function"){
18717                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18718             }
18719         }
18720         var n = this.queryParam === false ? 'node' : this.queryParam;
18721         buf.push(n + "=", encodeURIComponent(node.id));
18722         return buf.join("");
18723     },
18724
18725     requestData : function(node, callback){
18726         if(this.fireEvent("beforeload", this, node, callback) !== false){
18727             this.transId = Roo.Ajax.request({
18728                 method:this.requestMethod,
18729                 url: this.dataUrl||this.url,
18730                 success: this.handleResponse,
18731                 failure: this.handleFailure,
18732                 scope: this,
18733                 argument: {callback: callback, node: node},
18734                 params: this.getParams(node)
18735             });
18736         }else{
18737             // if the load is cancelled, make sure we notify
18738             // the node that we are done
18739             if(typeof callback == "function"){
18740                 callback();
18741             }
18742         }
18743     },
18744
18745     isLoading : function(){
18746         return this.transId ? true : false;
18747     },
18748
18749     abort : function(){
18750         if(this.isLoading()){
18751             Roo.Ajax.abort(this.transId);
18752         }
18753     },
18754
18755     // private
18756     createNode : function(attr)
18757     {
18758         // apply baseAttrs, nice idea Corey!
18759         if(this.baseAttrs){
18760             Roo.applyIf(attr, this.baseAttrs);
18761         }
18762         if(this.applyLoader !== false){
18763             attr.loader = this;
18764         }
18765         // uiProvider = depreciated..
18766         
18767         if(typeof(attr.uiProvider) == 'string'){
18768            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18769                 /**  eval:var:attr */ eval(attr.uiProvider);
18770         }
18771         if(typeof(this.uiProviders['default']) != 'undefined') {
18772             attr.uiProvider = this.uiProviders['default'];
18773         }
18774         
18775         this.fireEvent('create', this, attr);
18776         
18777         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18778         return(attr.leaf ?
18779                         new Roo.tree.TreeNode(attr) :
18780                         new Roo.tree.AsyncTreeNode(attr));
18781     },
18782
18783     processResponse : function(response, node, callback)
18784     {
18785         var json = response.responseText;
18786         try {
18787             
18788             var o = Roo.decode(json);
18789             
18790             if (this.root === false && typeof(o.success) != undefined) {
18791                 this.root = 'data'; // the default behaviour for list like data..
18792                 }
18793                 
18794             if (this.root !== false &&  !o.success) {
18795                 // it's a failure condition.
18796                 var a = response.argument;
18797                 this.fireEvent("loadexception", this, a.node, response);
18798                 Roo.log("Load failed - should have a handler really");
18799                 return;
18800             }
18801             
18802             
18803             
18804             if (this.root !== false) {
18805                  o = o[this.root];
18806             }
18807             
18808             for(var i = 0, len = o.length; i < len; i++){
18809                 var n = this.createNode(o[i]);
18810                 if(n){
18811                     node.appendChild(n);
18812                 }
18813             }
18814             if(typeof callback == "function"){
18815                 callback(this, node);
18816             }
18817         }catch(e){
18818             this.handleFailure(response);
18819         }
18820     },
18821
18822     handleResponse : function(response){
18823         this.transId = false;
18824         var a = response.argument;
18825         this.processResponse(response, a.node, a.callback);
18826         this.fireEvent("load", this, a.node, response);
18827     },
18828
18829     handleFailure : function(response)
18830     {
18831         // should handle failure better..
18832         this.transId = false;
18833         var a = response.argument;
18834         this.fireEvent("loadexception", this, a.node, response);
18835         if(typeof a.callback == "function"){
18836             a.callback(this, a.node);
18837         }
18838     }
18839 });/*
18840  * Based on:
18841  * Ext JS Library 1.1.1
18842  * Copyright(c) 2006-2007, Ext JS, LLC.
18843  *
18844  * Originally Released Under LGPL - original licence link has changed is not relivant.
18845  *
18846  * Fork - LGPL
18847  * <script type="text/javascript">
18848  */
18849
18850 /**
18851 * @class Roo.tree.TreeFilter
18852 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18853 * @param {TreePanel} tree
18854 * @param {Object} config (optional)
18855  */
18856 Roo.tree.TreeFilter = function(tree, config){
18857     this.tree = tree;
18858     this.filtered = {};
18859     Roo.apply(this, config);
18860 };
18861
18862 Roo.tree.TreeFilter.prototype = {
18863     clearBlank:false,
18864     reverse:false,
18865     autoClear:false,
18866     remove:false,
18867
18868      /**
18869      * Filter the data by a specific attribute.
18870      * @param {String/RegExp} value Either string that the attribute value
18871      * should start with or a RegExp to test against the attribute
18872      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18873      * @param {TreeNode} startNode (optional) The node to start the filter at.
18874      */
18875     filter : function(value, attr, startNode){
18876         attr = attr || "text";
18877         var f;
18878         if(typeof value == "string"){
18879             var vlen = value.length;
18880             // auto clear empty filter
18881             if(vlen == 0 && this.clearBlank){
18882                 this.clear();
18883                 return;
18884             }
18885             value = value.toLowerCase();
18886             f = function(n){
18887                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18888             };
18889         }else if(value.exec){ // regex?
18890             f = function(n){
18891                 return value.test(n.attributes[attr]);
18892             };
18893         }else{
18894             throw 'Illegal filter type, must be string or regex';
18895         }
18896         this.filterBy(f, null, startNode);
18897         },
18898
18899     /**
18900      * Filter by a function. The passed function will be called with each
18901      * node in the tree (or from the startNode). If the function returns true, the node is kept
18902      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18903      * @param {Function} fn The filter function
18904      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18905      */
18906     filterBy : function(fn, scope, startNode){
18907         startNode = startNode || this.tree.root;
18908         if(this.autoClear){
18909             this.clear();
18910         }
18911         var af = this.filtered, rv = this.reverse;
18912         var f = function(n){
18913             if(n == startNode){
18914                 return true;
18915             }
18916             if(af[n.id]){
18917                 return false;
18918             }
18919             var m = fn.call(scope || n, n);
18920             if(!m || rv){
18921                 af[n.id] = n;
18922                 n.ui.hide();
18923                 return false;
18924             }
18925             return true;
18926         };
18927         startNode.cascade(f);
18928         if(this.remove){
18929            for(var id in af){
18930                if(typeof id != "function"){
18931                    var n = af[id];
18932                    if(n && n.parentNode){
18933                        n.parentNode.removeChild(n);
18934                    }
18935                }
18936            }
18937         }
18938     },
18939
18940     /**
18941      * Clears the current filter. Note: with the "remove" option
18942      * set a filter cannot be cleared.
18943      */
18944     clear : function(){
18945         var t = this.tree;
18946         var af = this.filtered;
18947         for(var id in af){
18948             if(typeof id != "function"){
18949                 var n = af[id];
18950                 if(n){
18951                     n.ui.show();
18952                 }
18953             }
18954         }
18955         this.filtered = {};
18956     }
18957 };
18958 /*
18959  * Based on:
18960  * Ext JS Library 1.1.1
18961  * Copyright(c) 2006-2007, Ext JS, LLC.
18962  *
18963  * Originally Released Under LGPL - original licence link has changed is not relivant.
18964  *
18965  * Fork - LGPL
18966  * <script type="text/javascript">
18967  */
18968  
18969
18970 /**
18971  * @class Roo.tree.TreeSorter
18972  * Provides sorting of nodes in a TreePanel
18973  * 
18974  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18975  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18976  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18977  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18978  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18979  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18980  * @constructor
18981  * @param {TreePanel} tree
18982  * @param {Object} config
18983  */
18984 Roo.tree.TreeSorter = function(tree, config){
18985     Roo.apply(this, config);
18986     tree.on("beforechildrenrendered", this.doSort, this);
18987     tree.on("append", this.updateSort, this);
18988     tree.on("insert", this.updateSort, this);
18989     
18990     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18991     var p = this.property || "text";
18992     var sortType = this.sortType;
18993     var fs = this.folderSort;
18994     var cs = this.caseSensitive === true;
18995     var leafAttr = this.leafAttr || 'leaf';
18996
18997     this.sortFn = function(n1, n2){
18998         if(fs){
18999             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
19000                 return 1;
19001             }
19002             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
19003                 return -1;
19004             }
19005         }
19006         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19007         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19008         if(v1 < v2){
19009                         return dsc ? +1 : -1;
19010                 }else if(v1 > v2){
19011                         return dsc ? -1 : +1;
19012         }else{
19013                 return 0;
19014         }
19015     };
19016 };
19017
19018 Roo.tree.TreeSorter.prototype = {
19019     doSort : function(node){
19020         node.sort(this.sortFn);
19021     },
19022     
19023     compareNodes : function(n1, n2){
19024         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19025     },
19026     
19027     updateSort : function(tree, node){
19028         if(node.childrenRendered){
19029             this.doSort.defer(1, this, [node]);
19030         }
19031     }
19032 };/*
19033  * Based on:
19034  * Ext JS Library 1.1.1
19035  * Copyright(c) 2006-2007, Ext JS, LLC.
19036  *
19037  * Originally Released Under LGPL - original licence link has changed is not relivant.
19038  *
19039  * Fork - LGPL
19040  * <script type="text/javascript">
19041  */
19042
19043 if(Roo.dd.DropZone){
19044     
19045 Roo.tree.TreeDropZone = function(tree, config){
19046     this.allowParentInsert = false;
19047     this.allowContainerDrop = false;
19048     this.appendOnly = false;
19049     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19050     this.tree = tree;
19051     this.lastInsertClass = "x-tree-no-status";
19052     this.dragOverData = {};
19053 };
19054
19055 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19056     ddGroup : "TreeDD",
19057     scroll:  true,
19058     
19059     expandDelay : 1000,
19060     
19061     expandNode : function(node){
19062         if(node.hasChildNodes() && !node.isExpanded()){
19063             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19064         }
19065     },
19066     
19067     queueExpand : function(node){
19068         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19069     },
19070     
19071     cancelExpand : function(){
19072         if(this.expandProcId){
19073             clearTimeout(this.expandProcId);
19074             this.expandProcId = false;
19075         }
19076     },
19077     
19078     isValidDropPoint : function(n, pt, dd, e, data){
19079         if(!n || !data){ return false; }
19080         var targetNode = n.node;
19081         var dropNode = data.node;
19082         // default drop rules
19083         if(!(targetNode && targetNode.isTarget && pt)){
19084             return false;
19085         }
19086         if(pt == "append" && targetNode.allowChildren === false){
19087             return false;
19088         }
19089         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19090             return false;
19091         }
19092         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19093             return false;
19094         }
19095         // reuse the object
19096         var overEvent = this.dragOverData;
19097         overEvent.tree = this.tree;
19098         overEvent.target = targetNode;
19099         overEvent.data = data;
19100         overEvent.point = pt;
19101         overEvent.source = dd;
19102         overEvent.rawEvent = e;
19103         overEvent.dropNode = dropNode;
19104         overEvent.cancel = false;  
19105         var result = this.tree.fireEvent("nodedragover", overEvent);
19106         return overEvent.cancel === false && result !== false;
19107     },
19108     
19109     getDropPoint : function(e, n, dd)
19110     {
19111         var tn = n.node;
19112         if(tn.isRoot){
19113             return tn.allowChildren !== false ? "append" : false; // always append for root
19114         }
19115         var dragEl = n.ddel;
19116         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19117         var y = Roo.lib.Event.getPageY(e);
19118         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19119         
19120         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19121         var noAppend = tn.allowChildren === false;
19122         if(this.appendOnly || tn.parentNode.allowChildren === false){
19123             return noAppend ? false : "append";
19124         }
19125         var noBelow = false;
19126         if(!this.allowParentInsert){
19127             noBelow = tn.hasChildNodes() && tn.isExpanded();
19128         }
19129         var q = (b - t) / (noAppend ? 2 : 3);
19130         if(y >= t && y < (t + q)){
19131             return "above";
19132         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19133             return "below";
19134         }else{
19135             return "append";
19136         }
19137     },
19138     
19139     onNodeEnter : function(n, dd, e, data)
19140     {
19141         this.cancelExpand();
19142     },
19143     
19144     onNodeOver : function(n, dd, e, data)
19145     {
19146        
19147         var pt = this.getDropPoint(e, n, dd);
19148         var node = n.node;
19149         
19150         // auto node expand check
19151         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19152             this.queueExpand(node);
19153         }else if(pt != "append"){
19154             this.cancelExpand();
19155         }
19156         
19157         // set the insert point style on the target node
19158         var returnCls = this.dropNotAllowed;
19159         if(this.isValidDropPoint(n, pt, dd, e, data)){
19160            if(pt){
19161                var el = n.ddel;
19162                var cls;
19163                if(pt == "above"){
19164                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19165                    cls = "x-tree-drag-insert-above";
19166                }else if(pt == "below"){
19167                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19168                    cls = "x-tree-drag-insert-below";
19169                }else{
19170                    returnCls = "x-tree-drop-ok-append";
19171                    cls = "x-tree-drag-append";
19172                }
19173                if(this.lastInsertClass != cls){
19174                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19175                    this.lastInsertClass = cls;
19176                }
19177            }
19178        }
19179        return returnCls;
19180     },
19181     
19182     onNodeOut : function(n, dd, e, data){
19183         
19184         this.cancelExpand();
19185         this.removeDropIndicators(n);
19186     },
19187     
19188     onNodeDrop : function(n, dd, e, data){
19189         var point = this.getDropPoint(e, n, dd);
19190         var targetNode = n.node;
19191         targetNode.ui.startDrop();
19192         if(!this.isValidDropPoint(n, point, dd, e, data)){
19193             targetNode.ui.endDrop();
19194             return false;
19195         }
19196         // first try to find the drop node
19197         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19198         var dropEvent = {
19199             tree : this.tree,
19200             target: targetNode,
19201             data: data,
19202             point: point,
19203             source: dd,
19204             rawEvent: e,
19205             dropNode: dropNode,
19206             cancel: !dropNode   
19207         };
19208         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19209         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19210             targetNode.ui.endDrop();
19211             return false;
19212         }
19213         // allow target changing
19214         targetNode = dropEvent.target;
19215         if(point == "append" && !targetNode.isExpanded()){
19216             targetNode.expand(false, null, function(){
19217                 this.completeDrop(dropEvent);
19218             }.createDelegate(this));
19219         }else{
19220             this.completeDrop(dropEvent);
19221         }
19222         return true;
19223     },
19224     
19225     completeDrop : function(de){
19226         var ns = de.dropNode, p = de.point, t = de.target;
19227         if(!(ns instanceof Array)){
19228             ns = [ns];
19229         }
19230         var n;
19231         for(var i = 0, len = ns.length; i < len; i++){
19232             n = ns[i];
19233             if(p == "above"){
19234                 t.parentNode.insertBefore(n, t);
19235             }else if(p == "below"){
19236                 t.parentNode.insertBefore(n, t.nextSibling);
19237             }else{
19238                 t.appendChild(n);
19239             }
19240         }
19241         n.ui.focus();
19242         if(this.tree.hlDrop){
19243             n.ui.highlight();
19244         }
19245         t.ui.endDrop();
19246         this.tree.fireEvent("nodedrop", de);
19247     },
19248     
19249     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19250         if(this.tree.hlDrop){
19251             dropNode.ui.focus();
19252             dropNode.ui.highlight();
19253         }
19254         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19255     },
19256     
19257     getTree : function(){
19258         return this.tree;
19259     },
19260     
19261     removeDropIndicators : function(n){
19262         if(n && n.ddel){
19263             var el = n.ddel;
19264             Roo.fly(el).removeClass([
19265                     "x-tree-drag-insert-above",
19266                     "x-tree-drag-insert-below",
19267                     "x-tree-drag-append"]);
19268             this.lastInsertClass = "_noclass";
19269         }
19270     },
19271     
19272     beforeDragDrop : function(target, e, id){
19273         this.cancelExpand();
19274         return true;
19275     },
19276     
19277     afterRepair : function(data){
19278         if(data && Roo.enableFx){
19279             data.node.ui.highlight();
19280         }
19281         this.hideProxy();
19282     } 
19283     
19284 });
19285
19286 }
19287 /*
19288  * Based on:
19289  * Ext JS Library 1.1.1
19290  * Copyright(c) 2006-2007, Ext JS, LLC.
19291  *
19292  * Originally Released Under LGPL - original licence link has changed is not relivant.
19293  *
19294  * Fork - LGPL
19295  * <script type="text/javascript">
19296  */
19297  
19298
19299 if(Roo.dd.DragZone){
19300 Roo.tree.TreeDragZone = function(tree, config){
19301     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19302     this.tree = tree;
19303 };
19304
19305 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19306     ddGroup : "TreeDD",
19307    
19308     onBeforeDrag : function(data, e){
19309         var n = data.node;
19310         return n && n.draggable && !n.disabled;
19311     },
19312      
19313     
19314     onInitDrag : function(e){
19315         var data = this.dragData;
19316         this.tree.getSelectionModel().select(data.node);
19317         this.proxy.update("");
19318         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19319         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19320     },
19321     
19322     getRepairXY : function(e, data){
19323         return data.node.ui.getDDRepairXY();
19324     },
19325     
19326     onEndDrag : function(data, e){
19327         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19328         
19329         
19330     },
19331     
19332     onValidDrop : function(dd, e, id){
19333         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19334         this.hideProxy();
19335     },
19336     
19337     beforeInvalidDrop : function(e, id){
19338         // this scrolls the original position back into view
19339         var sm = this.tree.getSelectionModel();
19340         sm.clearSelections();
19341         sm.select(this.dragData.node);
19342     }
19343 });
19344 }/*
19345  * Based on:
19346  * Ext JS Library 1.1.1
19347  * Copyright(c) 2006-2007, Ext JS, LLC.
19348  *
19349  * Originally Released Under LGPL - original licence link has changed is not relivant.
19350  *
19351  * Fork - LGPL
19352  * <script type="text/javascript">
19353  */
19354 /**
19355  * @class Roo.tree.TreeEditor
19356  * @extends Roo.Editor
19357  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19358  * as the editor field.
19359  * @constructor
19360  * @param {Object} config (used to be the tree panel.)
19361  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19362  * 
19363  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19364  * @cfg {Roo.form.TextField|Object} field The field configuration
19365  *
19366  * 
19367  */
19368 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19369     var tree = config;
19370     var field;
19371     if (oldconfig) { // old style..
19372         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19373     } else {
19374         // new style..
19375         tree = config.tree;
19376         config.field = config.field  || {};
19377         config.field.xtype = 'TextField';
19378         field = Roo.factory(config.field, Roo.form);
19379     }
19380     config = config || {};
19381     
19382     
19383     this.addEvents({
19384         /**
19385          * @event beforenodeedit
19386          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19387          * false from the handler of this event.
19388          * @param {Editor} this
19389          * @param {Roo.tree.Node} node 
19390          */
19391         "beforenodeedit" : true
19392     });
19393     
19394     //Roo.log(config);
19395     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19396
19397     this.tree = tree;
19398
19399     tree.on('beforeclick', this.beforeNodeClick, this);
19400     tree.getTreeEl().on('mousedown', this.hide, this);
19401     this.on('complete', this.updateNode, this);
19402     this.on('beforestartedit', this.fitToTree, this);
19403     this.on('startedit', this.bindScroll, this, {delay:10});
19404     this.on('specialkey', this.onSpecialKey, this);
19405 };
19406
19407 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19408     /**
19409      * @cfg {String} alignment
19410      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19411      */
19412     alignment: "l-l",
19413     // inherit
19414     autoSize: false,
19415     /**
19416      * @cfg {Boolean} hideEl
19417      * True to hide the bound element while the editor is displayed (defaults to false)
19418      */
19419     hideEl : false,
19420     /**
19421      * @cfg {String} cls
19422      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19423      */
19424     cls: "x-small-editor x-tree-editor",
19425     /**
19426      * @cfg {Boolean} shim
19427      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19428      */
19429     shim:false,
19430     // inherit
19431     shadow:"frame",
19432     /**
19433      * @cfg {Number} maxWidth
19434      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19435      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19436      * scroll and client offsets into account prior to each edit.
19437      */
19438     maxWidth: 250,
19439
19440     editDelay : 350,
19441
19442     // private
19443     fitToTree : function(ed, el){
19444         var td = this.tree.getTreeEl().dom, nd = el.dom;
19445         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19446             td.scrollLeft = nd.offsetLeft;
19447         }
19448         var w = Math.min(
19449                 this.maxWidth,
19450                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19451         this.setSize(w, '');
19452         
19453         return this.fireEvent('beforenodeedit', this, this.editNode);
19454         
19455     },
19456
19457     // private
19458     triggerEdit : function(node){
19459         this.completeEdit();
19460         this.editNode = node;
19461         this.startEdit(node.ui.textNode, node.text);
19462     },
19463
19464     // private
19465     bindScroll : function(){
19466         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19467     },
19468
19469     // private
19470     beforeNodeClick : function(node, e){
19471         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19472         this.lastClick = new Date();
19473         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19474             e.stopEvent();
19475             this.triggerEdit(node);
19476             return false;
19477         }
19478         return true;
19479     },
19480
19481     // private
19482     updateNode : function(ed, value){
19483         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19484         this.editNode.setText(value);
19485     },
19486
19487     // private
19488     onHide : function(){
19489         Roo.tree.TreeEditor.superclass.onHide.call(this);
19490         if(this.editNode){
19491             this.editNode.ui.focus();
19492         }
19493     },
19494
19495     // private
19496     onSpecialKey : function(field, e){
19497         var k = e.getKey();
19498         if(k == e.ESC){
19499             e.stopEvent();
19500             this.cancelEdit();
19501         }else if(k == e.ENTER && !e.hasModifier()){
19502             e.stopEvent();
19503             this.completeEdit();
19504         }
19505     }
19506 });//<Script type="text/javascript">
19507 /*
19508  * Based on:
19509  * Ext JS Library 1.1.1
19510  * Copyright(c) 2006-2007, Ext JS, LLC.
19511  *
19512  * Originally Released Under LGPL - original licence link has changed is not relivant.
19513  *
19514  * Fork - LGPL
19515  * <script type="text/javascript">
19516  */
19517  
19518 /**
19519  * Not documented??? - probably should be...
19520  */
19521
19522 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19523     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19524     
19525     renderElements : function(n, a, targetNode, bulkRender){
19526         //consel.log("renderElements?");
19527         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19528
19529         var t = n.getOwnerTree();
19530         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19531         
19532         var cols = t.columns;
19533         var bw = t.borderWidth;
19534         var c = cols[0];
19535         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19536          var cb = typeof a.checked == "boolean";
19537         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19538         var colcls = 'x-t-' + tid + '-c0';
19539         var buf = [
19540             '<li class="x-tree-node">',
19541             
19542                 
19543                 '<div class="x-tree-node-el ', a.cls,'">',
19544                     // extran...
19545                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19546                 
19547                 
19548                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19549                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19550                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19551                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19552                            (a.iconCls ? ' '+a.iconCls : ''),
19553                            '" unselectable="on" />',
19554                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19555                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19556                              
19557                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19558                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19559                             '<span unselectable="on" qtip="' + tx + '">',
19560                              tx,
19561                              '</span></a>' ,
19562                     '</div>',
19563                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19564                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19565                  ];
19566         for(var i = 1, len = cols.length; i < len; i++){
19567             c = cols[i];
19568             colcls = 'x-t-' + tid + '-c' +i;
19569             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19570             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19571                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19572                       "</div>");
19573          }
19574          
19575          buf.push(
19576             '</a>',
19577             '<div class="x-clear"></div></div>',
19578             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19579             "</li>");
19580         
19581         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19582             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19583                                 n.nextSibling.ui.getEl(), buf.join(""));
19584         }else{
19585             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19586         }
19587         var el = this.wrap.firstChild;
19588         this.elRow = el;
19589         this.elNode = el.firstChild;
19590         this.ranchor = el.childNodes[1];
19591         this.ctNode = this.wrap.childNodes[1];
19592         var cs = el.firstChild.childNodes;
19593         this.indentNode = cs[0];
19594         this.ecNode = cs[1];
19595         this.iconNode = cs[2];
19596         var index = 3;
19597         if(cb){
19598             this.checkbox = cs[3];
19599             index++;
19600         }
19601         this.anchor = cs[index];
19602         
19603         this.textNode = cs[index].firstChild;
19604         
19605         //el.on("click", this.onClick, this);
19606         //el.on("dblclick", this.onDblClick, this);
19607         
19608         
19609        // console.log(this);
19610     },
19611     initEvents : function(){
19612         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19613         
19614             
19615         var a = this.ranchor;
19616
19617         var el = Roo.get(a);
19618
19619         if(Roo.isOpera){ // opera render bug ignores the CSS
19620             el.setStyle("text-decoration", "none");
19621         }
19622
19623         el.on("click", this.onClick, this);
19624         el.on("dblclick", this.onDblClick, this);
19625         el.on("contextmenu", this.onContextMenu, this);
19626         
19627     },
19628     
19629     /*onSelectedChange : function(state){
19630         if(state){
19631             this.focus();
19632             this.addClass("x-tree-selected");
19633         }else{
19634             //this.blur();
19635             this.removeClass("x-tree-selected");
19636         }
19637     },*/
19638     addClass : function(cls){
19639         if(this.elRow){
19640             Roo.fly(this.elRow).addClass(cls);
19641         }
19642         
19643     },
19644     
19645     
19646     removeClass : function(cls){
19647         if(this.elRow){
19648             Roo.fly(this.elRow).removeClass(cls);
19649         }
19650     }
19651
19652     
19653     
19654 });//<Script type="text/javascript">
19655
19656 /*
19657  * Based on:
19658  * Ext JS Library 1.1.1
19659  * Copyright(c) 2006-2007, Ext JS, LLC.
19660  *
19661  * Originally Released Under LGPL - original licence link has changed is not relivant.
19662  *
19663  * Fork - LGPL
19664  * <script type="text/javascript">
19665  */
19666  
19667
19668 /**
19669  * @class Roo.tree.ColumnTree
19670  * @extends Roo.data.TreePanel
19671  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19672  * @cfg {int} borderWidth  compined right/left border allowance
19673  * @constructor
19674  * @param {String/HTMLElement/Element} el The container element
19675  * @param {Object} config
19676  */
19677 Roo.tree.ColumnTree =  function(el, config)
19678 {
19679    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19680    this.addEvents({
19681         /**
19682         * @event resize
19683         * Fire this event on a container when it resizes
19684         * @param {int} w Width
19685         * @param {int} h Height
19686         */
19687        "resize" : true
19688     });
19689     this.on('resize', this.onResize, this);
19690 };
19691
19692 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19693     //lines:false,
19694     
19695     
19696     borderWidth: Roo.isBorderBox ? 0 : 2, 
19697     headEls : false,
19698     
19699     render : function(){
19700         // add the header.....
19701        
19702         Roo.tree.ColumnTree.superclass.render.apply(this);
19703         
19704         this.el.addClass('x-column-tree');
19705         
19706         this.headers = this.el.createChild(
19707             {cls:'x-tree-headers'},this.innerCt.dom);
19708    
19709         var cols = this.columns, c;
19710         var totalWidth = 0;
19711         this.headEls = [];
19712         var  len = cols.length;
19713         for(var i = 0; i < len; i++){
19714              c = cols[i];
19715              totalWidth += c.width;
19716             this.headEls.push(this.headers.createChild({
19717                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19718                  cn: {
19719                      cls:'x-tree-hd-text',
19720                      html: c.header
19721                  },
19722                  style:'width:'+(c.width-this.borderWidth)+'px;'
19723              }));
19724         }
19725         this.headers.createChild({cls:'x-clear'});
19726         // prevent floats from wrapping when clipped
19727         this.headers.setWidth(totalWidth);
19728         //this.innerCt.setWidth(totalWidth);
19729         this.innerCt.setStyle({ overflow: 'auto' });
19730         this.onResize(this.width, this.height);
19731              
19732         
19733     },
19734     onResize : function(w,h)
19735     {
19736         this.height = h;
19737         this.width = w;
19738         // resize cols..
19739         this.innerCt.setWidth(this.width);
19740         this.innerCt.setHeight(this.height-20);
19741         
19742         // headers...
19743         var cols = this.columns, c;
19744         var totalWidth = 0;
19745         var expEl = false;
19746         var len = cols.length;
19747         for(var i = 0; i < len; i++){
19748             c = cols[i];
19749             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19750                 // it's the expander..
19751                 expEl  = this.headEls[i];
19752                 continue;
19753             }
19754             totalWidth += c.width;
19755             
19756         }
19757         if (expEl) {
19758             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19759         }
19760         this.headers.setWidth(w-20);
19761
19762         
19763         
19764         
19765     }
19766 });
19767 /*
19768  * Based on:
19769  * Ext JS Library 1.1.1
19770  * Copyright(c) 2006-2007, Ext JS, LLC.
19771  *
19772  * Originally Released Under LGPL - original licence link has changed is not relivant.
19773  *
19774  * Fork - LGPL
19775  * <script type="text/javascript">
19776  */
19777  
19778 /**
19779  * @class Roo.menu.Menu
19780  * @extends Roo.util.Observable
19781  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19782  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19783  * @constructor
19784  * Creates a new Menu
19785  * @param {Object} config Configuration options
19786  */
19787 Roo.menu.Menu = function(config){
19788     Roo.apply(this, config);
19789     this.id = this.id || Roo.id();
19790     this.addEvents({
19791         /**
19792          * @event beforeshow
19793          * Fires before this menu is displayed
19794          * @param {Roo.menu.Menu} this
19795          */
19796         beforeshow : true,
19797         /**
19798          * @event beforehide
19799          * Fires before this menu is hidden
19800          * @param {Roo.menu.Menu} this
19801          */
19802         beforehide : true,
19803         /**
19804          * @event show
19805          * Fires after this menu is displayed
19806          * @param {Roo.menu.Menu} this
19807          */
19808         show : true,
19809         /**
19810          * @event hide
19811          * Fires after this menu is hidden
19812          * @param {Roo.menu.Menu} this
19813          */
19814         hide : true,
19815         /**
19816          * @event click
19817          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19818          * @param {Roo.menu.Menu} this
19819          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19820          * @param {Roo.EventObject} e
19821          */
19822         click : true,
19823         /**
19824          * @event mouseover
19825          * Fires when the mouse is hovering over this menu
19826          * @param {Roo.menu.Menu} this
19827          * @param {Roo.EventObject} e
19828          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19829          */
19830         mouseover : true,
19831         /**
19832          * @event mouseout
19833          * Fires when the mouse exits this menu
19834          * @param {Roo.menu.Menu} this
19835          * @param {Roo.EventObject} e
19836          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19837          */
19838         mouseout : true,
19839         /**
19840          * @event itemclick
19841          * Fires when a menu item contained in this menu is clicked
19842          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19843          * @param {Roo.EventObject} e
19844          */
19845         itemclick: true
19846     });
19847     if (this.registerMenu) {
19848         Roo.menu.MenuMgr.register(this);
19849     }
19850     
19851     var mis = this.items;
19852     this.items = new Roo.util.MixedCollection();
19853     if(mis){
19854         this.add.apply(this, mis);
19855     }
19856 };
19857
19858 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19859     /**
19860      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19861      */
19862     minWidth : 120,
19863     /**
19864      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19865      * for bottom-right shadow (defaults to "sides")
19866      */
19867     shadow : "sides",
19868     /**
19869      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19870      * this menu (defaults to "tl-tr?")
19871      */
19872     subMenuAlign : "tl-tr?",
19873     /**
19874      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19875      * relative to its element of origin (defaults to "tl-bl?")
19876      */
19877     defaultAlign : "tl-bl?",
19878     /**
19879      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19880      */
19881     allowOtherMenus : false,
19882     /**
19883      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19884      */
19885     registerMenu : true,
19886
19887     hidden:true,
19888
19889     // private
19890     render : function(){
19891         if(this.el){
19892             return;
19893         }
19894         var el = this.el = new Roo.Layer({
19895             cls: "x-menu",
19896             shadow:this.shadow,
19897             constrain: false,
19898             parentEl: this.parentEl || document.body,
19899             zindex:15000
19900         });
19901
19902         this.keyNav = new Roo.menu.MenuNav(this);
19903
19904         if(this.plain){
19905             el.addClass("x-menu-plain");
19906         }
19907         if(this.cls){
19908             el.addClass(this.cls);
19909         }
19910         // generic focus element
19911         this.focusEl = el.createChild({
19912             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19913         });
19914         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19915         ul.on("click", this.onClick, this);
19916         ul.on("mouseover", this.onMouseOver, this);
19917         ul.on("mouseout", this.onMouseOut, this);
19918         this.items.each(function(item){
19919             if (item.hidden) {
19920                 return;
19921             }
19922             
19923             var li = document.createElement("li");
19924             li.className = "x-menu-list-item";
19925             ul.dom.appendChild(li);
19926             item.render(li, this);
19927         }, this);
19928         this.ul = ul;
19929         this.autoWidth();
19930     },
19931
19932     // private
19933     autoWidth : function(){
19934         var el = this.el, ul = this.ul;
19935         if(!el){
19936             return;
19937         }
19938         var w = this.width;
19939         if(w){
19940             el.setWidth(w);
19941         }else if(Roo.isIE){
19942             el.setWidth(this.minWidth);
19943             var t = el.dom.offsetWidth; // force recalc
19944             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19945         }
19946     },
19947
19948     // private
19949     delayAutoWidth : function(){
19950         if(this.rendered){
19951             if(!this.awTask){
19952                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19953             }
19954             this.awTask.delay(20);
19955         }
19956     },
19957
19958     // private
19959     findTargetItem : function(e){
19960         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19961         if(t && t.menuItemId){
19962             return this.items.get(t.menuItemId);
19963         }
19964     },
19965
19966     // private
19967     onClick : function(e){
19968         var t;
19969         if(t = this.findTargetItem(e)){
19970             t.onClick(e);
19971             this.fireEvent("click", this, t, e);
19972         }
19973     },
19974
19975     // private
19976     setActiveItem : function(item, autoExpand){
19977         if(item != this.activeItem){
19978             if(this.activeItem){
19979                 this.activeItem.deactivate();
19980             }
19981             this.activeItem = item;
19982             item.activate(autoExpand);
19983         }else if(autoExpand){
19984             item.expandMenu();
19985         }
19986     },
19987
19988     // private
19989     tryActivate : function(start, step){
19990         var items = this.items;
19991         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19992             var item = items.get(i);
19993             if(!item.disabled && item.canActivate){
19994                 this.setActiveItem(item, false);
19995                 return item;
19996             }
19997         }
19998         return false;
19999     },
20000
20001     // private
20002     onMouseOver : function(e){
20003         var t;
20004         if(t = this.findTargetItem(e)){
20005             if(t.canActivate && !t.disabled){
20006                 this.setActiveItem(t, true);
20007             }
20008         }
20009         this.fireEvent("mouseover", this, e, t);
20010     },
20011
20012     // private
20013     onMouseOut : function(e){
20014         var t;
20015         if(t = this.findTargetItem(e)){
20016             if(t == this.activeItem && t.shouldDeactivate(e)){
20017                 this.activeItem.deactivate();
20018                 delete this.activeItem;
20019             }
20020         }
20021         this.fireEvent("mouseout", this, e, t);
20022     },
20023
20024     /**
20025      * Read-only.  Returns true if the menu is currently displayed, else false.
20026      * @type Boolean
20027      */
20028     isVisible : function(){
20029         return this.el && !this.hidden;
20030     },
20031
20032     /**
20033      * Displays this menu relative to another element
20034      * @param {String/HTMLElement/Roo.Element} element The element to align to
20035      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20036      * the element (defaults to this.defaultAlign)
20037      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20038      */
20039     show : function(el, pos, parentMenu){
20040         this.parentMenu = parentMenu;
20041         if(!this.el){
20042             this.render();
20043         }
20044         this.fireEvent("beforeshow", this);
20045         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20046     },
20047
20048     /**
20049      * Displays this menu at a specific xy position
20050      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20051      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20052      */
20053     showAt : function(xy, parentMenu, /* private: */_e){
20054         this.parentMenu = parentMenu;
20055         if(!this.el){
20056             this.render();
20057         }
20058         if(_e !== false){
20059             this.fireEvent("beforeshow", this);
20060             xy = this.el.adjustForConstraints(xy);
20061         }
20062         this.el.setXY(xy);
20063         this.el.show();
20064         this.hidden = false;
20065         this.focus();
20066         this.fireEvent("show", this);
20067     },
20068
20069     focus : function(){
20070         if(!this.hidden){
20071             this.doFocus.defer(50, this);
20072         }
20073     },
20074
20075     doFocus : function(){
20076         if(!this.hidden){
20077             this.focusEl.focus();
20078         }
20079     },
20080
20081     /**
20082      * Hides this menu and optionally all parent menus
20083      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20084      */
20085     hide : function(deep){
20086         if(this.el && this.isVisible()){
20087             this.fireEvent("beforehide", this);
20088             if(this.activeItem){
20089                 this.activeItem.deactivate();
20090                 this.activeItem = null;
20091             }
20092             this.el.hide();
20093             this.hidden = true;
20094             this.fireEvent("hide", this);
20095         }
20096         if(deep === true && this.parentMenu){
20097             this.parentMenu.hide(true);
20098         }
20099     },
20100
20101     /**
20102      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20103      * Any of the following are valid:
20104      * <ul>
20105      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20106      * <li>An HTMLElement object which will be converted to a menu item</li>
20107      * <li>A menu item config object that will be created as a new menu item</li>
20108      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20109      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20110      * </ul>
20111      * Usage:
20112      * <pre><code>
20113 // Create the menu
20114 var menu = new Roo.menu.Menu();
20115
20116 // Create a menu item to add by reference
20117 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20118
20119 // Add a bunch of items at once using different methods.
20120 // Only the last item added will be returned.
20121 var item = menu.add(
20122     menuItem,                // add existing item by ref
20123     'Dynamic Item',          // new TextItem
20124     '-',                     // new separator
20125     { text: 'Config Item' }  // new item by config
20126 );
20127 </code></pre>
20128      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20129      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20130      */
20131     add : function(){
20132         var a = arguments, l = a.length, item;
20133         for(var i = 0; i < l; i++){
20134             var el = a[i];
20135             if ((typeof(el) == "object") && el.xtype && el.xns) {
20136                 el = Roo.factory(el, Roo.menu);
20137             }
20138             
20139             if(el.render){ // some kind of Item
20140                 item = this.addItem(el);
20141             }else if(typeof el == "string"){ // string
20142                 if(el == "separator" || el == "-"){
20143                     item = this.addSeparator();
20144                 }else{
20145                     item = this.addText(el);
20146                 }
20147             }else if(el.tagName || el.el){ // element
20148                 item = this.addElement(el);
20149             }else if(typeof el == "object"){ // must be menu item config?
20150                 item = this.addMenuItem(el);
20151             }
20152         }
20153         return item;
20154     },
20155
20156     /**
20157      * Returns this menu's underlying {@link Roo.Element} object
20158      * @return {Roo.Element} The element
20159      */
20160     getEl : function(){
20161         if(!this.el){
20162             this.render();
20163         }
20164         return this.el;
20165     },
20166
20167     /**
20168      * Adds a separator bar to the menu
20169      * @return {Roo.menu.Item} The menu item that was added
20170      */
20171     addSeparator : function(){
20172         return this.addItem(new Roo.menu.Separator());
20173     },
20174
20175     /**
20176      * Adds an {@link Roo.Element} object to the menu
20177      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20178      * @return {Roo.menu.Item} The menu item that was added
20179      */
20180     addElement : function(el){
20181         return this.addItem(new Roo.menu.BaseItem(el));
20182     },
20183
20184     /**
20185      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20186      * @param {Roo.menu.Item} item The menu item to add
20187      * @return {Roo.menu.Item} The menu item that was added
20188      */
20189     addItem : function(item){
20190         this.items.add(item);
20191         if(this.ul){
20192             var li = document.createElement("li");
20193             li.className = "x-menu-list-item";
20194             this.ul.dom.appendChild(li);
20195             item.render(li, this);
20196             this.delayAutoWidth();
20197         }
20198         return item;
20199     },
20200
20201     /**
20202      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20203      * @param {Object} config A MenuItem config object
20204      * @return {Roo.menu.Item} The menu item that was added
20205      */
20206     addMenuItem : function(config){
20207         if(!(config instanceof Roo.menu.Item)){
20208             if(typeof config.checked == "boolean"){ // must be check menu item config?
20209                 config = new Roo.menu.CheckItem(config);
20210             }else{
20211                 config = new Roo.menu.Item(config);
20212             }
20213         }
20214         return this.addItem(config);
20215     },
20216
20217     /**
20218      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20219      * @param {String} text The text to display in the menu item
20220      * @return {Roo.menu.Item} The menu item that was added
20221      */
20222     addText : function(text){
20223         return this.addItem(new Roo.menu.TextItem({ text : text }));
20224     },
20225
20226     /**
20227      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20228      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20229      * @param {Roo.menu.Item} item The menu item to add
20230      * @return {Roo.menu.Item} The menu item that was added
20231      */
20232     insert : function(index, item){
20233         this.items.insert(index, item);
20234         if(this.ul){
20235             var li = document.createElement("li");
20236             li.className = "x-menu-list-item";
20237             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20238             item.render(li, this);
20239             this.delayAutoWidth();
20240         }
20241         return item;
20242     },
20243
20244     /**
20245      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20246      * @param {Roo.menu.Item} item The menu item to remove
20247      */
20248     remove : function(item){
20249         this.items.removeKey(item.id);
20250         item.destroy();
20251     },
20252
20253     /**
20254      * Removes and destroys all items in the menu
20255      */
20256     removeAll : function(){
20257         var f;
20258         while(f = this.items.first()){
20259             this.remove(f);
20260         }
20261     }
20262 });
20263
20264 // MenuNav is a private utility class used internally by the Menu
20265 Roo.menu.MenuNav = function(menu){
20266     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20267     this.scope = this.menu = menu;
20268 };
20269
20270 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20271     doRelay : function(e, h){
20272         var k = e.getKey();
20273         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20274             this.menu.tryActivate(0, 1);
20275             return false;
20276         }
20277         return h.call(this.scope || this, e, this.menu);
20278     },
20279
20280     up : function(e, m){
20281         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20282             m.tryActivate(m.items.length-1, -1);
20283         }
20284     },
20285
20286     down : function(e, m){
20287         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20288             m.tryActivate(0, 1);
20289         }
20290     },
20291
20292     right : function(e, m){
20293         if(m.activeItem){
20294             m.activeItem.expandMenu(true);
20295         }
20296     },
20297
20298     left : function(e, m){
20299         m.hide();
20300         if(m.parentMenu && m.parentMenu.activeItem){
20301             m.parentMenu.activeItem.activate();
20302         }
20303     },
20304
20305     enter : function(e, m){
20306         if(m.activeItem){
20307             e.stopPropagation();
20308             m.activeItem.onClick(e);
20309             m.fireEvent("click", this, m.activeItem);
20310             return true;
20311         }
20312     }
20313 });/*
20314  * Based on:
20315  * Ext JS Library 1.1.1
20316  * Copyright(c) 2006-2007, Ext JS, LLC.
20317  *
20318  * Originally Released Under LGPL - original licence link has changed is not relivant.
20319  *
20320  * Fork - LGPL
20321  * <script type="text/javascript">
20322  */
20323  
20324 /**
20325  * @class Roo.menu.MenuMgr
20326  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20327  * @singleton
20328  */
20329 Roo.menu.MenuMgr = function(){
20330    var menus, active, groups = {}, attached = false, lastShow = new Date();
20331
20332    // private - called when first menu is created
20333    function init(){
20334        menus = {};
20335        active = new Roo.util.MixedCollection();
20336        Roo.get(document).addKeyListener(27, function(){
20337            if(active.length > 0){
20338                hideAll();
20339            }
20340        });
20341    }
20342
20343    // private
20344    function hideAll(){
20345        if(active && active.length > 0){
20346            var c = active.clone();
20347            c.each(function(m){
20348                m.hide();
20349            });
20350        }
20351    }
20352
20353    // private
20354    function onHide(m){
20355        active.remove(m);
20356        if(active.length < 1){
20357            Roo.get(document).un("mousedown", onMouseDown);
20358            attached = false;
20359        }
20360    }
20361
20362    // private
20363    function onShow(m){
20364        var last = active.last();
20365        lastShow = new Date();
20366        active.add(m);
20367        if(!attached){
20368            Roo.get(document).on("mousedown", onMouseDown);
20369            attached = true;
20370        }
20371        if(m.parentMenu){
20372           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20373           m.parentMenu.activeChild = m;
20374        }else if(last && last.isVisible()){
20375           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20376        }
20377    }
20378
20379    // private
20380    function onBeforeHide(m){
20381        if(m.activeChild){
20382            m.activeChild.hide();
20383        }
20384        if(m.autoHideTimer){
20385            clearTimeout(m.autoHideTimer);
20386            delete m.autoHideTimer;
20387        }
20388    }
20389
20390    // private
20391    function onBeforeShow(m){
20392        var pm = m.parentMenu;
20393        if(!pm && !m.allowOtherMenus){
20394            hideAll();
20395        }else if(pm && pm.activeChild && active != m){
20396            pm.activeChild.hide();
20397        }
20398    }
20399
20400    // private
20401    function onMouseDown(e){
20402        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20403            hideAll();
20404        }
20405    }
20406
20407    // private
20408    function onBeforeCheck(mi, state){
20409        if(state){
20410            var g = groups[mi.group];
20411            for(var i = 0, l = g.length; i < l; i++){
20412                if(g[i] != mi){
20413                    g[i].setChecked(false);
20414                }
20415            }
20416        }
20417    }
20418
20419    return {
20420
20421        /**
20422         * Hides all menus that are currently visible
20423         */
20424        hideAll : function(){
20425             hideAll();  
20426        },
20427
20428        // private
20429        register : function(menu){
20430            if(!menus){
20431                init();
20432            }
20433            menus[menu.id] = menu;
20434            menu.on("beforehide", onBeforeHide);
20435            menu.on("hide", onHide);
20436            menu.on("beforeshow", onBeforeShow);
20437            menu.on("show", onShow);
20438            var g = menu.group;
20439            if(g && menu.events["checkchange"]){
20440                if(!groups[g]){
20441                    groups[g] = [];
20442                }
20443                groups[g].push(menu);
20444                menu.on("checkchange", onCheck);
20445            }
20446        },
20447
20448         /**
20449          * Returns a {@link Roo.menu.Menu} object
20450          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20451          * be used to generate and return a new Menu instance.
20452          */
20453        get : function(menu){
20454            if(typeof menu == "string"){ // menu id
20455                return menus[menu];
20456            }else if(menu.events){  // menu instance
20457                return menu;
20458            }else if(typeof menu.length == 'number'){ // array of menu items?
20459                return new Roo.menu.Menu({items:menu});
20460            }else{ // otherwise, must be a config
20461                return new Roo.menu.Menu(menu);
20462            }
20463        },
20464
20465        // private
20466        unregister : function(menu){
20467            delete menus[menu.id];
20468            menu.un("beforehide", onBeforeHide);
20469            menu.un("hide", onHide);
20470            menu.un("beforeshow", onBeforeShow);
20471            menu.un("show", onShow);
20472            var g = menu.group;
20473            if(g && menu.events["checkchange"]){
20474                groups[g].remove(menu);
20475                menu.un("checkchange", onCheck);
20476            }
20477        },
20478
20479        // private
20480        registerCheckable : function(menuItem){
20481            var g = menuItem.group;
20482            if(g){
20483                if(!groups[g]){
20484                    groups[g] = [];
20485                }
20486                groups[g].push(menuItem);
20487                menuItem.on("beforecheckchange", onBeforeCheck);
20488            }
20489        },
20490
20491        // private
20492        unregisterCheckable : function(menuItem){
20493            var g = menuItem.group;
20494            if(g){
20495                groups[g].remove(menuItem);
20496                menuItem.un("beforecheckchange", onBeforeCheck);
20497            }
20498        }
20499    };
20500 }();/*
20501  * Based on:
20502  * Ext JS Library 1.1.1
20503  * Copyright(c) 2006-2007, Ext JS, LLC.
20504  *
20505  * Originally Released Under LGPL - original licence link has changed is not relivant.
20506  *
20507  * Fork - LGPL
20508  * <script type="text/javascript">
20509  */
20510  
20511
20512 /**
20513  * @class Roo.menu.BaseItem
20514  * @extends Roo.Component
20515  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20516  * management and base configuration options shared by all menu components.
20517  * @constructor
20518  * Creates a new BaseItem
20519  * @param {Object} config Configuration options
20520  */
20521 Roo.menu.BaseItem = function(config){
20522     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20523
20524     this.addEvents({
20525         /**
20526          * @event click
20527          * Fires when this item is clicked
20528          * @param {Roo.menu.BaseItem} this
20529          * @param {Roo.EventObject} e
20530          */
20531         click: true,
20532         /**
20533          * @event activate
20534          * Fires when this item is activated
20535          * @param {Roo.menu.BaseItem} this
20536          */
20537         activate : true,
20538         /**
20539          * @event deactivate
20540          * Fires when this item is deactivated
20541          * @param {Roo.menu.BaseItem} this
20542          */
20543         deactivate : true
20544     });
20545
20546     if(this.handler){
20547         this.on("click", this.handler, this.scope, true);
20548     }
20549 };
20550
20551 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20552     /**
20553      * @cfg {Function} handler
20554      * A function that will handle the click event of this menu item (defaults to undefined)
20555      */
20556     /**
20557      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20558      */
20559     canActivate : false,
20560     
20561      /**
20562      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20563      */
20564     hidden: false,
20565     
20566     /**
20567      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20568      */
20569     activeClass : "x-menu-item-active",
20570     /**
20571      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20572      */
20573     hideOnClick : true,
20574     /**
20575      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20576      */
20577     hideDelay : 100,
20578
20579     // private
20580     ctype: "Roo.menu.BaseItem",
20581
20582     // private
20583     actionMode : "container",
20584
20585     // private
20586     render : function(container, parentMenu){
20587         this.parentMenu = parentMenu;
20588         Roo.menu.BaseItem.superclass.render.call(this, container);
20589         this.container.menuItemId = this.id;
20590     },
20591
20592     // private
20593     onRender : function(container, position){
20594         this.el = Roo.get(this.el);
20595         container.dom.appendChild(this.el.dom);
20596     },
20597
20598     // private
20599     onClick : function(e){
20600         if(!this.disabled && this.fireEvent("click", this, e) !== false
20601                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20602             this.handleClick(e);
20603         }else{
20604             e.stopEvent();
20605         }
20606     },
20607
20608     // private
20609     activate : function(){
20610         if(this.disabled){
20611             return false;
20612         }
20613         var li = this.container;
20614         li.addClass(this.activeClass);
20615         this.region = li.getRegion().adjust(2, 2, -2, -2);
20616         this.fireEvent("activate", this);
20617         return true;
20618     },
20619
20620     // private
20621     deactivate : function(){
20622         this.container.removeClass(this.activeClass);
20623         this.fireEvent("deactivate", this);
20624     },
20625
20626     // private
20627     shouldDeactivate : function(e){
20628         return !this.region || !this.region.contains(e.getPoint());
20629     },
20630
20631     // private
20632     handleClick : function(e){
20633         if(this.hideOnClick){
20634             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20635         }
20636     },
20637
20638     // private
20639     expandMenu : function(autoActivate){
20640         // do nothing
20641     },
20642
20643     // private
20644     hideMenu : function(){
20645         // do nothing
20646     }
20647 });/*
20648  * Based on:
20649  * Ext JS Library 1.1.1
20650  * Copyright(c) 2006-2007, Ext JS, LLC.
20651  *
20652  * Originally Released Under LGPL - original licence link has changed is not relivant.
20653  *
20654  * Fork - LGPL
20655  * <script type="text/javascript">
20656  */
20657  
20658 /**
20659  * @class Roo.menu.Adapter
20660  * @extends Roo.menu.BaseItem
20661  * 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.
20662  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20663  * @constructor
20664  * Creates a new Adapter
20665  * @param {Object} config Configuration options
20666  */
20667 Roo.menu.Adapter = function(component, config){
20668     Roo.menu.Adapter.superclass.constructor.call(this, config);
20669     this.component = component;
20670 };
20671 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20672     // private
20673     canActivate : true,
20674
20675     // private
20676     onRender : function(container, position){
20677         this.component.render(container);
20678         this.el = this.component.getEl();
20679     },
20680
20681     // private
20682     activate : function(){
20683         if(this.disabled){
20684             return false;
20685         }
20686         this.component.focus();
20687         this.fireEvent("activate", this);
20688         return true;
20689     },
20690
20691     // private
20692     deactivate : function(){
20693         this.fireEvent("deactivate", this);
20694     },
20695
20696     // private
20697     disable : function(){
20698         this.component.disable();
20699         Roo.menu.Adapter.superclass.disable.call(this);
20700     },
20701
20702     // private
20703     enable : function(){
20704         this.component.enable();
20705         Roo.menu.Adapter.superclass.enable.call(this);
20706     }
20707 });/*
20708  * Based on:
20709  * Ext JS Library 1.1.1
20710  * Copyright(c) 2006-2007, Ext JS, LLC.
20711  *
20712  * Originally Released Under LGPL - original licence link has changed is not relivant.
20713  *
20714  * Fork - LGPL
20715  * <script type="text/javascript">
20716  */
20717
20718 /**
20719  * @class Roo.menu.TextItem
20720  * @extends Roo.menu.BaseItem
20721  * Adds a static text string to a menu, usually used as either a heading or group separator.
20722  * Note: old style constructor with text is still supported.
20723  * 
20724  * @constructor
20725  * Creates a new TextItem
20726  * @param {Object} cfg Configuration
20727  */
20728 Roo.menu.TextItem = function(cfg){
20729     if (typeof(cfg) == 'string') {
20730         this.text = cfg;
20731     } else {
20732         Roo.apply(this,cfg);
20733     }
20734     
20735     Roo.menu.TextItem.superclass.constructor.call(this);
20736 };
20737
20738 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20739     /**
20740      * @cfg {Boolean} text Text to show on item.
20741      */
20742     text : '',
20743     
20744     /**
20745      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20746      */
20747     hideOnClick : false,
20748     /**
20749      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20750      */
20751     itemCls : "x-menu-text",
20752
20753     // private
20754     onRender : function(){
20755         var s = document.createElement("span");
20756         s.className = this.itemCls;
20757         s.innerHTML = this.text;
20758         this.el = s;
20759         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20760     }
20761 });/*
20762  * Based on:
20763  * Ext JS Library 1.1.1
20764  * Copyright(c) 2006-2007, Ext JS, LLC.
20765  *
20766  * Originally Released Under LGPL - original licence link has changed is not relivant.
20767  *
20768  * Fork - LGPL
20769  * <script type="text/javascript">
20770  */
20771
20772 /**
20773  * @class Roo.menu.Separator
20774  * @extends Roo.menu.BaseItem
20775  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20776  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20777  * @constructor
20778  * @param {Object} config Configuration options
20779  */
20780 Roo.menu.Separator = function(config){
20781     Roo.menu.Separator.superclass.constructor.call(this, config);
20782 };
20783
20784 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20785     /**
20786      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20787      */
20788     itemCls : "x-menu-sep",
20789     /**
20790      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20791      */
20792     hideOnClick : false,
20793
20794     // private
20795     onRender : function(li){
20796         var s = document.createElement("span");
20797         s.className = this.itemCls;
20798         s.innerHTML = "&#160;";
20799         this.el = s;
20800         li.addClass("x-menu-sep-li");
20801         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20802     }
20803 });/*
20804  * Based on:
20805  * Ext JS Library 1.1.1
20806  * Copyright(c) 2006-2007, Ext JS, LLC.
20807  *
20808  * Originally Released Under LGPL - original licence link has changed is not relivant.
20809  *
20810  * Fork - LGPL
20811  * <script type="text/javascript">
20812  */
20813 /**
20814  * @class Roo.menu.Item
20815  * @extends Roo.menu.BaseItem
20816  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20817  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20818  * activation and click handling.
20819  * @constructor
20820  * Creates a new Item
20821  * @param {Object} config Configuration options
20822  */
20823 Roo.menu.Item = function(config){
20824     Roo.menu.Item.superclass.constructor.call(this, config);
20825     if(this.menu){
20826         this.menu = Roo.menu.MenuMgr.get(this.menu);
20827     }
20828 };
20829 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20830     
20831     /**
20832      * @cfg {String} text
20833      * The text to show on the menu item.
20834      */
20835     text: '',
20836      /**
20837      * @cfg {String} HTML to render in menu
20838      * The text to show on the menu item (HTML version).
20839      */
20840     html: '',
20841     /**
20842      * @cfg {String} icon
20843      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20844      */
20845     icon: undefined,
20846     /**
20847      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20848      */
20849     itemCls : "x-menu-item",
20850     /**
20851      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20852      */
20853     canActivate : true,
20854     /**
20855      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20856      */
20857     showDelay: 200,
20858     // doc'd in BaseItem
20859     hideDelay: 200,
20860
20861     // private
20862     ctype: "Roo.menu.Item",
20863     
20864     // private
20865     onRender : function(container, position){
20866         var el = document.createElement("a");
20867         el.hideFocus = true;
20868         el.unselectable = "on";
20869         el.href = this.href || "#";
20870         if(this.hrefTarget){
20871             el.target = this.hrefTarget;
20872         }
20873         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20874         
20875         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20876         
20877         el.innerHTML = String.format(
20878                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20879                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20880         this.el = el;
20881         Roo.menu.Item.superclass.onRender.call(this, container, position);
20882     },
20883
20884     /**
20885      * Sets the text to display in this menu item
20886      * @param {String} text The text to display
20887      * @param {Boolean} isHTML true to indicate text is pure html.
20888      */
20889     setText : function(text, isHTML){
20890         if (isHTML) {
20891             this.html = text;
20892         } else {
20893             this.text = text;
20894             this.html = '';
20895         }
20896         if(this.rendered){
20897             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20898      
20899             this.el.update(String.format(
20900                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20901                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20902             this.parentMenu.autoWidth();
20903         }
20904     },
20905
20906     // private
20907     handleClick : function(e){
20908         if(!this.href){ // if no link defined, stop the event automatically
20909             e.stopEvent();
20910         }
20911         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20912     },
20913
20914     // private
20915     activate : function(autoExpand){
20916         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20917             this.focus();
20918             if(autoExpand){
20919                 this.expandMenu();
20920             }
20921         }
20922         return true;
20923     },
20924
20925     // private
20926     shouldDeactivate : function(e){
20927         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20928             if(this.menu && this.menu.isVisible()){
20929                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20930             }
20931             return true;
20932         }
20933         return false;
20934     },
20935
20936     // private
20937     deactivate : function(){
20938         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20939         this.hideMenu();
20940     },
20941
20942     // private
20943     expandMenu : function(autoActivate){
20944         if(!this.disabled && this.menu){
20945             clearTimeout(this.hideTimer);
20946             delete this.hideTimer;
20947             if(!this.menu.isVisible() && !this.showTimer){
20948                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20949             }else if (this.menu.isVisible() && autoActivate){
20950                 this.menu.tryActivate(0, 1);
20951             }
20952         }
20953     },
20954
20955     // private
20956     deferExpand : function(autoActivate){
20957         delete this.showTimer;
20958         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20959         if(autoActivate){
20960             this.menu.tryActivate(0, 1);
20961         }
20962     },
20963
20964     // private
20965     hideMenu : function(){
20966         clearTimeout(this.showTimer);
20967         delete this.showTimer;
20968         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20969             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20970         }
20971     },
20972
20973     // private
20974     deferHide : function(){
20975         delete this.hideTimer;
20976         this.menu.hide();
20977     }
20978 });/*
20979  * Based on:
20980  * Ext JS Library 1.1.1
20981  * Copyright(c) 2006-2007, Ext JS, LLC.
20982  *
20983  * Originally Released Under LGPL - original licence link has changed is not relivant.
20984  *
20985  * Fork - LGPL
20986  * <script type="text/javascript">
20987  */
20988  
20989 /**
20990  * @class Roo.menu.CheckItem
20991  * @extends Roo.menu.Item
20992  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20993  * @constructor
20994  * Creates a new CheckItem
20995  * @param {Object} config Configuration options
20996  */
20997 Roo.menu.CheckItem = function(config){
20998     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20999     this.addEvents({
21000         /**
21001          * @event beforecheckchange
21002          * Fires before the checked value is set, providing an opportunity to cancel if needed
21003          * @param {Roo.menu.CheckItem} this
21004          * @param {Boolean} checked The new checked value that will be set
21005          */
21006         "beforecheckchange" : true,
21007         /**
21008          * @event checkchange
21009          * Fires after the checked value has been set
21010          * @param {Roo.menu.CheckItem} this
21011          * @param {Boolean} checked The checked value that was set
21012          */
21013         "checkchange" : true
21014     });
21015     if(this.checkHandler){
21016         this.on('checkchange', this.checkHandler, this.scope);
21017     }
21018 };
21019 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21020     /**
21021      * @cfg {String} group
21022      * All check items with the same group name will automatically be grouped into a single-select
21023      * radio button group (defaults to '')
21024      */
21025     /**
21026      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21027      */
21028     itemCls : "x-menu-item x-menu-check-item",
21029     /**
21030      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21031      */
21032     groupClass : "x-menu-group-item",
21033
21034     /**
21035      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21036      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21037      * initialized with checked = true will be rendered as checked.
21038      */
21039     checked: false,
21040
21041     // private
21042     ctype: "Roo.menu.CheckItem",
21043
21044     // private
21045     onRender : function(c){
21046         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21047         if(this.group){
21048             this.el.addClass(this.groupClass);
21049         }
21050         Roo.menu.MenuMgr.registerCheckable(this);
21051         if(this.checked){
21052             this.checked = false;
21053             this.setChecked(true, true);
21054         }
21055     },
21056
21057     // private
21058     destroy : function(){
21059         if(this.rendered){
21060             Roo.menu.MenuMgr.unregisterCheckable(this);
21061         }
21062         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21063     },
21064
21065     /**
21066      * Set the checked state of this item
21067      * @param {Boolean} checked The new checked value
21068      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21069      */
21070     setChecked : function(state, suppressEvent){
21071         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21072             if(this.container){
21073                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21074             }
21075             this.checked = state;
21076             if(suppressEvent !== true){
21077                 this.fireEvent("checkchange", this, state);
21078             }
21079         }
21080     },
21081
21082     // private
21083     handleClick : function(e){
21084        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21085            this.setChecked(!this.checked);
21086        }
21087        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21088     }
21089 });/*
21090  * Based on:
21091  * Ext JS Library 1.1.1
21092  * Copyright(c) 2006-2007, Ext JS, LLC.
21093  *
21094  * Originally Released Under LGPL - original licence link has changed is not relivant.
21095  *
21096  * Fork - LGPL
21097  * <script type="text/javascript">
21098  */
21099  
21100 /**
21101  * @class Roo.menu.DateItem
21102  * @extends Roo.menu.Adapter
21103  * A menu item that wraps the {@link Roo.DatPicker} component.
21104  * @constructor
21105  * Creates a new DateItem
21106  * @param {Object} config Configuration options
21107  */
21108 Roo.menu.DateItem = function(config){
21109     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21110     /** The Roo.DatePicker object @type Roo.DatePicker */
21111     this.picker = this.component;
21112     this.addEvents({select: true});
21113     
21114     this.picker.on("render", function(picker){
21115         picker.getEl().swallowEvent("click");
21116         picker.container.addClass("x-menu-date-item");
21117     });
21118
21119     this.picker.on("select", this.onSelect, this);
21120 };
21121
21122 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21123     // private
21124     onSelect : function(picker, date){
21125         this.fireEvent("select", this, date, picker);
21126         Roo.menu.DateItem.superclass.handleClick.call(this);
21127     }
21128 });/*
21129  * Based on:
21130  * Ext JS Library 1.1.1
21131  * Copyright(c) 2006-2007, Ext JS, LLC.
21132  *
21133  * Originally Released Under LGPL - original licence link has changed is not relivant.
21134  *
21135  * Fork - LGPL
21136  * <script type="text/javascript">
21137  */
21138  
21139 /**
21140  * @class Roo.menu.ColorItem
21141  * @extends Roo.menu.Adapter
21142  * A menu item that wraps the {@link Roo.ColorPalette} component.
21143  * @constructor
21144  * Creates a new ColorItem
21145  * @param {Object} config Configuration options
21146  */
21147 Roo.menu.ColorItem = function(config){
21148     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21149     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21150     this.palette = this.component;
21151     this.relayEvents(this.palette, ["select"]);
21152     if(this.selectHandler){
21153         this.on('select', this.selectHandler, this.scope);
21154     }
21155 };
21156 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21157  * Based on:
21158  * Ext JS Library 1.1.1
21159  * Copyright(c) 2006-2007, Ext JS, LLC.
21160  *
21161  * Originally Released Under LGPL - original licence link has changed is not relivant.
21162  *
21163  * Fork - LGPL
21164  * <script type="text/javascript">
21165  */
21166  
21167
21168 /**
21169  * @class Roo.menu.DateMenu
21170  * @extends Roo.menu.Menu
21171  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21172  * @constructor
21173  * Creates a new DateMenu
21174  * @param {Object} config Configuration options
21175  */
21176 Roo.menu.DateMenu = function(config){
21177     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21178     this.plain = true;
21179     var di = new Roo.menu.DateItem(config);
21180     this.add(di);
21181     /**
21182      * The {@link Roo.DatePicker} instance for this DateMenu
21183      * @type DatePicker
21184      */
21185     this.picker = di.picker;
21186     /**
21187      * @event select
21188      * @param {DatePicker} picker
21189      * @param {Date} date
21190      */
21191     this.relayEvents(di, ["select"]);
21192     this.on('beforeshow', function(){
21193         if(this.picker){
21194             this.picker.hideMonthPicker(false);
21195         }
21196     }, this);
21197 };
21198 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21199     cls:'x-date-menu'
21200 });/*
21201  * Based on:
21202  * Ext JS Library 1.1.1
21203  * Copyright(c) 2006-2007, Ext JS, LLC.
21204  *
21205  * Originally Released Under LGPL - original licence link has changed is not relivant.
21206  *
21207  * Fork - LGPL
21208  * <script type="text/javascript">
21209  */
21210  
21211
21212 /**
21213  * @class Roo.menu.ColorMenu
21214  * @extends Roo.menu.Menu
21215  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21216  * @constructor
21217  * Creates a new ColorMenu
21218  * @param {Object} config Configuration options
21219  */
21220 Roo.menu.ColorMenu = function(config){
21221     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21222     this.plain = true;
21223     var ci = new Roo.menu.ColorItem(config);
21224     this.add(ci);
21225     /**
21226      * The {@link Roo.ColorPalette} instance for this ColorMenu
21227      * @type ColorPalette
21228      */
21229     this.palette = ci.palette;
21230     /**
21231      * @event select
21232      * @param {ColorPalette} palette
21233      * @param {String} color
21234      */
21235     this.relayEvents(ci, ["select"]);
21236 };
21237 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21238  * Based on:
21239  * Ext JS Library 1.1.1
21240  * Copyright(c) 2006-2007, Ext JS, LLC.
21241  *
21242  * Originally Released Under LGPL - original licence link has changed is not relivant.
21243  *
21244  * Fork - LGPL
21245  * <script type="text/javascript">
21246  */
21247  
21248 /**
21249  * @class Roo.form.Field
21250  * @extends Roo.BoxComponent
21251  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21252  * @constructor
21253  * Creates a new Field
21254  * @param {Object} config Configuration options
21255  */
21256 Roo.form.Field = function(config){
21257     Roo.form.Field.superclass.constructor.call(this, config);
21258 };
21259
21260 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21261     /**
21262      * @cfg {String} fieldLabel Label to use when rendering a form.
21263      */
21264        /**
21265      * @cfg {String} qtip Mouse over tip
21266      */
21267      
21268     /**
21269      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21270      */
21271     invalidClass : "x-form-invalid",
21272     /**
21273      * @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")
21274      */
21275     invalidText : "The value in this field is invalid",
21276     /**
21277      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21278      */
21279     focusClass : "x-form-focus",
21280     /**
21281      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21282       automatic validation (defaults to "keyup").
21283      */
21284     validationEvent : "keyup",
21285     /**
21286      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21287      */
21288     validateOnBlur : true,
21289     /**
21290      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21291      */
21292     validationDelay : 250,
21293     /**
21294      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21295      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21296      */
21297     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21298     /**
21299      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21300      */
21301     fieldClass : "x-form-field",
21302     /**
21303      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21304      *<pre>
21305 Value         Description
21306 -----------   ----------------------------------------------------------------------
21307 qtip          Display a quick tip when the user hovers over the field
21308 title         Display a default browser title attribute popup
21309 under         Add a block div beneath the field containing the error text
21310 side          Add an error icon to the right of the field with a popup on hover
21311 [element id]  Add the error text directly to the innerHTML of the specified element
21312 </pre>
21313      */
21314     msgTarget : 'qtip',
21315     /**
21316      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21317      */
21318     msgFx : 'normal',
21319
21320     /**
21321      * @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.
21322      */
21323     readOnly : false,
21324
21325     /**
21326      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21327      */
21328     disabled : false,
21329
21330     /**
21331      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21332      */
21333     inputType : undefined,
21334     
21335     /**
21336      * @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).
21337          */
21338         tabIndex : undefined,
21339         
21340     // private
21341     isFormField : true,
21342
21343     // private
21344     hasFocus : false,
21345     /**
21346      * @property {Roo.Element} fieldEl
21347      * Element Containing the rendered Field (with label etc.)
21348      */
21349     /**
21350      * @cfg {Mixed} value A value to initialize this field with.
21351      */
21352     value : undefined,
21353
21354     /**
21355      * @cfg {String} name The field's HTML name attribute.
21356      */
21357     /**
21358      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21359      */
21360
21361         // private ??
21362         initComponent : function(){
21363         Roo.form.Field.superclass.initComponent.call(this);
21364         this.addEvents({
21365             /**
21366              * @event focus
21367              * Fires when this field receives input focus.
21368              * @param {Roo.form.Field} this
21369              */
21370             focus : true,
21371             /**
21372              * @event blur
21373              * Fires when this field loses input focus.
21374              * @param {Roo.form.Field} this
21375              */
21376             blur : true,
21377             /**
21378              * @event specialkey
21379              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21380              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21381              * @param {Roo.form.Field} this
21382              * @param {Roo.EventObject} e The event object
21383              */
21384             specialkey : true,
21385             /**
21386              * @event change
21387              * Fires just before the field blurs if the field value has changed.
21388              * @param {Roo.form.Field} this
21389              * @param {Mixed} newValue The new value
21390              * @param {Mixed} oldValue The original value
21391              */
21392             change : true,
21393             /**
21394              * @event invalid
21395              * Fires after the field has been marked as invalid.
21396              * @param {Roo.form.Field} this
21397              * @param {String} msg The validation message
21398              */
21399             invalid : true,
21400             /**
21401              * @event valid
21402              * Fires after the field has been validated with no errors.
21403              * @param {Roo.form.Field} this
21404              */
21405             valid : true,
21406              /**
21407              * @event keyup
21408              * Fires after the key up
21409              * @param {Roo.form.Field} this
21410              * @param {Roo.EventObject}  e The event Object
21411              */
21412             keyup : true
21413         });
21414     },
21415
21416     /**
21417      * Returns the name attribute of the field if available
21418      * @return {String} name The field name
21419      */
21420     getName: function(){
21421          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21422     },
21423
21424     // private
21425     onRender : function(ct, position){
21426         Roo.form.Field.superclass.onRender.call(this, ct, position);
21427         if(!this.el){
21428             var cfg = this.getAutoCreate();
21429             if(!cfg.name){
21430                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21431             }
21432             if (!cfg.name.length) {
21433                 delete cfg.name;
21434             }
21435             if(this.inputType){
21436                 cfg.type = this.inputType;
21437             }
21438             this.el = ct.createChild(cfg, position);
21439         }
21440         var type = this.el.dom.type;
21441         if(type){
21442             if(type == 'password'){
21443                 type = 'text';
21444             }
21445             this.el.addClass('x-form-'+type);
21446         }
21447         if(this.readOnly){
21448             this.el.dom.readOnly = true;
21449         }
21450         if(this.tabIndex !== undefined){
21451             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21452         }
21453
21454         this.el.addClass([this.fieldClass, this.cls]);
21455         this.initValue();
21456     },
21457
21458     /**
21459      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21460      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21461      * @return {Roo.form.Field} this
21462      */
21463     applyTo : function(target){
21464         this.allowDomMove = false;
21465         this.el = Roo.get(target);
21466         this.render(this.el.dom.parentNode);
21467         return this;
21468     },
21469
21470     // private
21471     initValue : function(){
21472         if(this.value !== undefined){
21473             this.setValue(this.value);
21474         }else if(this.el.dom.value.length > 0){
21475             this.setValue(this.el.dom.value);
21476         }
21477     },
21478
21479     /**
21480      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21481      */
21482     isDirty : function() {
21483         if(this.disabled) {
21484             return false;
21485         }
21486         return String(this.getValue()) !== String(this.originalValue);
21487     },
21488
21489     // private
21490     afterRender : function(){
21491         Roo.form.Field.superclass.afterRender.call(this);
21492         this.initEvents();
21493     },
21494
21495     // private
21496     fireKey : function(e){
21497         //Roo.log('field ' + e.getKey());
21498         if(e.isNavKeyPress()){
21499             this.fireEvent("specialkey", this, e);
21500         }
21501     },
21502
21503     /**
21504      * Resets the current field value to the originally loaded value and clears any validation messages
21505      */
21506     reset : function(){
21507         this.setValue(this.originalValue);
21508         this.clearInvalid();
21509     },
21510
21511     // private
21512     initEvents : function(){
21513         // safari killled keypress - so keydown is now used..
21514         this.el.on("keydown" , this.fireKey,  this);
21515         this.el.on("focus", this.onFocus,  this);
21516         this.el.on("blur", this.onBlur,  this);
21517         this.el.relayEvent('keyup', this);
21518
21519         // reference to original value for reset
21520         this.originalValue = this.getValue();
21521     },
21522
21523     // private
21524     onFocus : function(){
21525         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21526             this.el.addClass(this.focusClass);
21527         }
21528         if(!this.hasFocus){
21529             this.hasFocus = true;
21530             this.startValue = this.getValue();
21531             this.fireEvent("focus", this);
21532         }
21533     },
21534
21535     beforeBlur : Roo.emptyFn,
21536
21537     // private
21538     onBlur : function(){
21539         this.beforeBlur();
21540         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21541             this.el.removeClass(this.focusClass);
21542         }
21543         this.hasFocus = false;
21544         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21545             this.validate();
21546         }
21547         var v = this.getValue();
21548         if(String(v) !== String(this.startValue)){
21549             this.fireEvent('change', this, v, this.startValue);
21550         }
21551         this.fireEvent("blur", this);
21552     },
21553
21554     /**
21555      * Returns whether or not the field value is currently valid
21556      * @param {Boolean} preventMark True to disable marking the field invalid
21557      * @return {Boolean} True if the value is valid, else false
21558      */
21559     isValid : function(preventMark){
21560         if(this.disabled){
21561             return true;
21562         }
21563         var restore = this.preventMark;
21564         this.preventMark = preventMark === true;
21565         var v = this.validateValue(this.processValue(this.getRawValue()));
21566         this.preventMark = restore;
21567         return v;
21568     },
21569
21570     /**
21571      * Validates the field value
21572      * @return {Boolean} True if the value is valid, else false
21573      */
21574     validate : function(){
21575         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21576             this.clearInvalid();
21577             return true;
21578         }
21579         return false;
21580     },
21581
21582     processValue : function(value){
21583         return value;
21584     },
21585
21586     // private
21587     // Subclasses should provide the validation implementation by overriding this
21588     validateValue : function(value){
21589         return true;
21590     },
21591
21592     /**
21593      * Mark this field as invalid
21594      * @param {String} msg The validation message
21595      */
21596     markInvalid : function(msg){
21597         if(!this.rendered || this.preventMark){ // not rendered
21598             return;
21599         }
21600         this.el.addClass(this.invalidClass);
21601         msg = msg || this.invalidText;
21602         switch(this.msgTarget){
21603             case 'qtip':
21604                 this.el.dom.qtip = msg;
21605                 this.el.dom.qclass = 'x-form-invalid-tip';
21606                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21607                     Roo.QuickTips.enable();
21608                 }
21609                 break;
21610             case 'title':
21611                 this.el.dom.title = msg;
21612                 break;
21613             case 'under':
21614                 if(!this.errorEl){
21615                     var elp = this.el.findParent('.x-form-element', 5, true);
21616                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21617                     this.errorEl.setWidth(elp.getWidth(true)-20);
21618                 }
21619                 this.errorEl.update(msg);
21620                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21621                 break;
21622             case 'side':
21623                 if(!this.errorIcon){
21624                     var elp = this.el.findParent('.x-form-element', 5, true);
21625                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21626                 }
21627                 this.alignErrorIcon();
21628                 this.errorIcon.dom.qtip = msg;
21629                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21630                 this.errorIcon.show();
21631                 this.on('resize', this.alignErrorIcon, this);
21632                 break;
21633             default:
21634                 var t = Roo.getDom(this.msgTarget);
21635                 t.innerHTML = msg;
21636                 t.style.display = this.msgDisplay;
21637                 break;
21638         }
21639         this.fireEvent('invalid', this, msg);
21640     },
21641
21642     // private
21643     alignErrorIcon : function(){
21644         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21645     },
21646
21647     /**
21648      * Clear any invalid styles/messages for this field
21649      */
21650     clearInvalid : function(){
21651         if(!this.rendered || this.preventMark){ // not rendered
21652             return;
21653         }
21654         this.el.removeClass(this.invalidClass);
21655         switch(this.msgTarget){
21656             case 'qtip':
21657                 this.el.dom.qtip = '';
21658                 break;
21659             case 'title':
21660                 this.el.dom.title = '';
21661                 break;
21662             case 'under':
21663                 if(this.errorEl){
21664                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21665                 }
21666                 break;
21667             case 'side':
21668                 if(this.errorIcon){
21669                     this.errorIcon.dom.qtip = '';
21670                     this.errorIcon.hide();
21671                     this.un('resize', this.alignErrorIcon, this);
21672                 }
21673                 break;
21674             default:
21675                 var t = Roo.getDom(this.msgTarget);
21676                 t.innerHTML = '';
21677                 t.style.display = 'none';
21678                 break;
21679         }
21680         this.fireEvent('valid', this);
21681     },
21682
21683     /**
21684      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21685      * @return {Mixed} value The field value
21686      */
21687     getRawValue : function(){
21688         var v = this.el.getValue();
21689         
21690         return v;
21691     },
21692
21693     /**
21694      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21695      * @return {Mixed} value The field value
21696      */
21697     getValue : function(){
21698         var v = this.el.getValue();
21699          
21700         return v;
21701     },
21702
21703     /**
21704      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21705      * @param {Mixed} value The value to set
21706      */
21707     setRawValue : function(v){
21708         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21709     },
21710
21711     /**
21712      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21713      * @param {Mixed} value The value to set
21714      */
21715     setValue : function(v){
21716         this.value = v;
21717         if(this.rendered){
21718             this.el.dom.value = (v === null || v === undefined ? '' : v);
21719              this.validate();
21720         }
21721     },
21722
21723     adjustSize : function(w, h){
21724         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21725         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21726         return s;
21727     },
21728
21729     adjustWidth : function(tag, w){
21730         tag = tag.toLowerCase();
21731         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21732             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21733                 if(tag == 'input'){
21734                     return w + 2;
21735                 }
21736                 if(tag == 'textarea'){
21737                     return w-2;
21738                 }
21739             }else if(Roo.isOpera){
21740                 if(tag == 'input'){
21741                     return w + 2;
21742                 }
21743                 if(tag == 'textarea'){
21744                     return w-2;
21745                 }
21746             }
21747         }
21748         return w;
21749     }
21750 });
21751
21752
21753 // anything other than normal should be considered experimental
21754 Roo.form.Field.msgFx = {
21755     normal : {
21756         show: function(msgEl, f){
21757             msgEl.setDisplayed('block');
21758         },
21759
21760         hide : function(msgEl, f){
21761             msgEl.setDisplayed(false).update('');
21762         }
21763     },
21764
21765     slide : {
21766         show: function(msgEl, f){
21767             msgEl.slideIn('t', {stopFx:true});
21768         },
21769
21770         hide : function(msgEl, f){
21771             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21772         }
21773     },
21774
21775     slideRight : {
21776         show: function(msgEl, f){
21777             msgEl.fixDisplay();
21778             msgEl.alignTo(f.el, 'tl-tr');
21779             msgEl.slideIn('l', {stopFx:true});
21780         },
21781
21782         hide : function(msgEl, f){
21783             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21784         }
21785     }
21786 };/*
21787  * Based on:
21788  * Ext JS Library 1.1.1
21789  * Copyright(c) 2006-2007, Ext JS, LLC.
21790  *
21791  * Originally Released Under LGPL - original licence link has changed is not relivant.
21792  *
21793  * Fork - LGPL
21794  * <script type="text/javascript">
21795  */
21796  
21797
21798 /**
21799  * @class Roo.form.TextField
21800  * @extends Roo.form.Field
21801  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21802  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21803  * @constructor
21804  * Creates a new TextField
21805  * @param {Object} config Configuration options
21806  */
21807 Roo.form.TextField = function(config){
21808     Roo.form.TextField.superclass.constructor.call(this, config);
21809     this.addEvents({
21810         /**
21811          * @event autosize
21812          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21813          * according to the default logic, but this event provides a hook for the developer to apply additional
21814          * logic at runtime to resize the field if needed.
21815              * @param {Roo.form.Field} this This text field
21816              * @param {Number} width The new field width
21817              */
21818         autosize : true
21819     });
21820 };
21821
21822 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21823     /**
21824      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21825      */
21826     grow : false,
21827     /**
21828      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21829      */
21830     growMin : 30,
21831     /**
21832      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21833      */
21834     growMax : 800,
21835     /**
21836      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21837      */
21838     vtype : null,
21839     /**
21840      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21841      */
21842     maskRe : null,
21843     /**
21844      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21845      */
21846     disableKeyFilter : false,
21847     /**
21848      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21849      */
21850     allowBlank : true,
21851     /**
21852      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21853      */
21854     minLength : 0,
21855     /**
21856      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21857      */
21858     maxLength : Number.MAX_VALUE,
21859     /**
21860      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21861      */
21862     minLengthText : "The minimum length for this field is {0}",
21863     /**
21864      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21865      */
21866     maxLengthText : "The maximum length for this field is {0}",
21867     /**
21868      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21869      */
21870     selectOnFocus : false,
21871     /**
21872      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21873      */
21874     blankText : "This field is required",
21875     /**
21876      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21877      * If available, this function will be called only after the basic validators all return true, and will be passed the
21878      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21879      */
21880     validator : null,
21881     /**
21882      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21883      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21884      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21885      */
21886     regex : null,
21887     /**
21888      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21889      */
21890     regexText : "",
21891     /**
21892      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21893      */
21894     emptyText : null,
21895    
21896
21897     // private
21898     initEvents : function()
21899     {
21900         if (this.emptyText) {
21901             this.el.attr('placeholder', this.emptyText);
21902         }
21903         
21904         Roo.form.TextField.superclass.initEvents.call(this);
21905         if(this.validationEvent == 'keyup'){
21906             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21907             this.el.on('keyup', this.filterValidation, this);
21908         }
21909         else if(this.validationEvent !== false){
21910             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21911         }
21912         
21913         if(this.selectOnFocus){
21914             this.on("focus", this.preFocus, this);
21915             
21916         }
21917         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21918             this.el.on("keypress", this.filterKeys, this);
21919         }
21920         if(this.grow){
21921             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21922             this.el.on("click", this.autoSize,  this);
21923         }
21924         if(this.el.is('input[type=password]') && Roo.isSafari){
21925             this.el.on('keydown', this.SafariOnKeyDown, this);
21926         }
21927     },
21928
21929     processValue : function(value){
21930         if(this.stripCharsRe){
21931             var newValue = value.replace(this.stripCharsRe, '');
21932             if(newValue !== value){
21933                 this.setRawValue(newValue);
21934                 return newValue;
21935             }
21936         }
21937         return value;
21938     },
21939
21940     filterValidation : function(e){
21941         if(!e.isNavKeyPress()){
21942             this.validationTask.delay(this.validationDelay);
21943         }
21944     },
21945
21946     // private
21947     onKeyUp : function(e){
21948         if(!e.isNavKeyPress()){
21949             this.autoSize();
21950         }
21951     },
21952
21953     /**
21954      * Resets the current field value to the originally-loaded value and clears any validation messages.
21955      *  
21956      */
21957     reset : function(){
21958         Roo.form.TextField.superclass.reset.call(this);
21959        
21960     },
21961
21962     
21963     // private
21964     preFocus : function(){
21965         
21966         if(this.selectOnFocus){
21967             this.el.dom.select();
21968         }
21969     },
21970
21971     
21972     // private
21973     filterKeys : function(e){
21974         var k = e.getKey();
21975         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21976             return;
21977         }
21978         var c = e.getCharCode(), cc = String.fromCharCode(c);
21979         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21980             return;
21981         }
21982         if(!this.maskRe.test(cc)){
21983             e.stopEvent();
21984         }
21985     },
21986
21987     setValue : function(v){
21988         
21989         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21990         
21991         this.autoSize();
21992     },
21993
21994     /**
21995      * Validates a value according to the field's validation rules and marks the field as invalid
21996      * if the validation fails
21997      * @param {Mixed} value The value to validate
21998      * @return {Boolean} True if the value is valid, else false
21999      */
22000     validateValue : function(value){
22001         if(value.length < 1)  { // if it's blank
22002              if(this.allowBlank){
22003                 this.clearInvalid();
22004                 return true;
22005              }else{
22006                 this.markInvalid(this.blankText);
22007                 return false;
22008              }
22009         }
22010         if(value.length < this.minLength){
22011             this.markInvalid(String.format(this.minLengthText, this.minLength));
22012             return false;
22013         }
22014         if(value.length > this.maxLength){
22015             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22016             return false;
22017         }
22018         if(this.vtype){
22019             var vt = Roo.form.VTypes;
22020             if(!vt[this.vtype](value, this)){
22021                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22022                 return false;
22023             }
22024         }
22025         if(typeof this.validator == "function"){
22026             var msg = this.validator(value);
22027             if(msg !== true){
22028                 this.markInvalid(msg);
22029                 return false;
22030             }
22031         }
22032         if(this.regex && !this.regex.test(value)){
22033             this.markInvalid(this.regexText);
22034             return false;
22035         }
22036         return true;
22037     },
22038
22039     /**
22040      * Selects text in this field
22041      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22042      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22043      */
22044     selectText : function(start, end){
22045         var v = this.getRawValue();
22046         if(v.length > 0){
22047             start = start === undefined ? 0 : start;
22048             end = end === undefined ? v.length : end;
22049             var d = this.el.dom;
22050             if(d.setSelectionRange){
22051                 d.setSelectionRange(start, end);
22052             }else if(d.createTextRange){
22053                 var range = d.createTextRange();
22054                 range.moveStart("character", start);
22055                 range.moveEnd("character", v.length-end);
22056                 range.select();
22057             }
22058         }
22059     },
22060
22061     /**
22062      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22063      * This only takes effect if grow = true, and fires the autosize event.
22064      */
22065     autoSize : function(){
22066         if(!this.grow || !this.rendered){
22067             return;
22068         }
22069         if(!this.metrics){
22070             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22071         }
22072         var el = this.el;
22073         var v = el.dom.value;
22074         var d = document.createElement('div');
22075         d.appendChild(document.createTextNode(v));
22076         v = d.innerHTML;
22077         d = null;
22078         v += "&#160;";
22079         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22080         this.el.setWidth(w);
22081         this.fireEvent("autosize", this, w);
22082     },
22083     
22084     // private
22085     SafariOnKeyDown : function(event)
22086     {
22087         // this is a workaround for a password hang bug on chrome/ webkit.
22088         
22089         var isSelectAll = false;
22090         
22091         if(this.el.dom.selectionEnd > 0){
22092             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22093         }
22094         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22095             event.preventDefault();
22096             this.setValue('');
22097             return;
22098         }
22099         
22100         if(isSelectAll){ // backspace and delete key
22101             
22102             event.preventDefault();
22103             // this is very hacky as keydown always get's upper case.
22104             //
22105             var cc = String.fromCharCode(event.getCharCode());
22106             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22107             
22108         }
22109         
22110         
22111     }
22112 });/*
22113  * Based on:
22114  * Ext JS Library 1.1.1
22115  * Copyright(c) 2006-2007, Ext JS, LLC.
22116  *
22117  * Originally Released Under LGPL - original licence link has changed is not relivant.
22118  *
22119  * Fork - LGPL
22120  * <script type="text/javascript">
22121  */
22122  
22123 /**
22124  * @class Roo.form.Hidden
22125  * @extends Roo.form.TextField
22126  * Simple Hidden element used on forms 
22127  * 
22128  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22129  * 
22130  * @constructor
22131  * Creates a new Hidden form element.
22132  * @param {Object} config Configuration options
22133  */
22134
22135
22136
22137 // easy hidden field...
22138 Roo.form.Hidden = function(config){
22139     Roo.form.Hidden.superclass.constructor.call(this, config);
22140 };
22141   
22142 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22143     fieldLabel:      '',
22144     inputType:      'hidden',
22145     width:          50,
22146     allowBlank:     true,
22147     labelSeparator: '',
22148     hidden:         true,
22149     itemCls :       'x-form-item-display-none'
22150
22151
22152 });
22153
22154
22155 /*
22156  * Based on:
22157  * Ext JS Library 1.1.1
22158  * Copyright(c) 2006-2007, Ext JS, LLC.
22159  *
22160  * Originally Released Under LGPL - original licence link has changed is not relivant.
22161  *
22162  * Fork - LGPL
22163  * <script type="text/javascript">
22164  */
22165  
22166 /**
22167  * @class Roo.form.TriggerField
22168  * @extends Roo.form.TextField
22169  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22170  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22171  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22172  * for which you can provide a custom implementation.  For example:
22173  * <pre><code>
22174 var trigger = new Roo.form.TriggerField();
22175 trigger.onTriggerClick = myTriggerFn;
22176 trigger.applyTo('my-field');
22177 </code></pre>
22178  *
22179  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22180  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22181  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22182  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22183  * @constructor
22184  * Create a new TriggerField.
22185  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22186  * to the base TextField)
22187  */
22188 Roo.form.TriggerField = function(config){
22189     this.mimicing = false;
22190     Roo.form.TriggerField.superclass.constructor.call(this, config);
22191 };
22192
22193 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22194     /**
22195      * @cfg {String} triggerClass A CSS class to apply to the trigger
22196      */
22197     /**
22198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22199      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22200      */
22201     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22202     /**
22203      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22204      */
22205     hideTrigger:false,
22206
22207     /** @cfg {Boolean} grow @hide */
22208     /** @cfg {Number} growMin @hide */
22209     /** @cfg {Number} growMax @hide */
22210
22211     /**
22212      * @hide 
22213      * @method
22214      */
22215     autoSize: Roo.emptyFn,
22216     // private
22217     monitorTab : true,
22218     // private
22219     deferHeight : true,
22220
22221     
22222     actionMode : 'wrap',
22223     // private
22224     onResize : function(w, h){
22225         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22226         if(typeof w == 'number'){
22227             var x = w - this.trigger.getWidth();
22228             this.el.setWidth(this.adjustWidth('input', x));
22229             this.trigger.setStyle('left', x+'px');
22230         }
22231     },
22232
22233     // private
22234     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22235
22236     // private
22237     getResizeEl : function(){
22238         return this.wrap;
22239     },
22240
22241     // private
22242     getPositionEl : function(){
22243         return this.wrap;
22244     },
22245
22246     // private
22247     alignErrorIcon : function(){
22248         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22249     },
22250
22251     // private
22252     onRender : function(ct, position){
22253         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22254         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22255         this.trigger = this.wrap.createChild(this.triggerConfig ||
22256                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22257         if(this.hideTrigger){
22258             this.trigger.setDisplayed(false);
22259         }
22260         this.initTrigger();
22261         if(!this.width){
22262             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22263         }
22264     },
22265
22266     // private
22267     initTrigger : function(){
22268         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22269         this.trigger.addClassOnOver('x-form-trigger-over');
22270         this.trigger.addClassOnClick('x-form-trigger-click');
22271     },
22272
22273     // private
22274     onDestroy : function(){
22275         if(this.trigger){
22276             this.trigger.removeAllListeners();
22277             this.trigger.remove();
22278         }
22279         if(this.wrap){
22280             this.wrap.remove();
22281         }
22282         Roo.form.TriggerField.superclass.onDestroy.call(this);
22283     },
22284
22285     // private
22286     onFocus : function(){
22287         Roo.form.TriggerField.superclass.onFocus.call(this);
22288         if(!this.mimicing){
22289             this.wrap.addClass('x-trigger-wrap-focus');
22290             this.mimicing = true;
22291             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22292             if(this.monitorTab){
22293                 this.el.on("keydown", this.checkTab, this);
22294             }
22295         }
22296     },
22297
22298     // private
22299     checkTab : function(e){
22300         if(e.getKey() == e.TAB){
22301             this.triggerBlur();
22302         }
22303     },
22304
22305     // private
22306     onBlur : function(){
22307         // do nothing
22308     },
22309
22310     // private
22311     mimicBlur : function(e, t){
22312         if(!this.wrap.contains(t) && this.validateBlur()){
22313             this.triggerBlur();
22314         }
22315     },
22316
22317     // private
22318     triggerBlur : function(){
22319         this.mimicing = false;
22320         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22321         if(this.monitorTab){
22322             this.el.un("keydown", this.checkTab, this);
22323         }
22324         this.wrap.removeClass('x-trigger-wrap-focus');
22325         Roo.form.TriggerField.superclass.onBlur.call(this);
22326     },
22327
22328     // private
22329     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22330     validateBlur : function(e, t){
22331         return true;
22332     },
22333
22334     // private
22335     onDisable : function(){
22336         Roo.form.TriggerField.superclass.onDisable.call(this);
22337         if(this.wrap){
22338             this.wrap.addClass('x-item-disabled');
22339         }
22340     },
22341
22342     // private
22343     onEnable : function(){
22344         Roo.form.TriggerField.superclass.onEnable.call(this);
22345         if(this.wrap){
22346             this.wrap.removeClass('x-item-disabled');
22347         }
22348     },
22349
22350     // private
22351     onShow : function(){
22352         var ae = this.getActionEl();
22353         
22354         if(ae){
22355             ae.dom.style.display = '';
22356             ae.dom.style.visibility = 'visible';
22357         }
22358     },
22359
22360     // private
22361     
22362     onHide : function(){
22363         var ae = this.getActionEl();
22364         ae.dom.style.display = 'none';
22365     },
22366
22367     /**
22368      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22369      * by an implementing function.
22370      * @method
22371      * @param {EventObject} e
22372      */
22373     onTriggerClick : Roo.emptyFn
22374 });
22375
22376 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22377 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22378 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22379 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22380     initComponent : function(){
22381         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22382
22383         this.triggerConfig = {
22384             tag:'span', cls:'x-form-twin-triggers', cn:[
22385             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22386             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22387         ]};
22388     },
22389
22390     getTrigger : function(index){
22391         return this.triggers[index];
22392     },
22393
22394     initTrigger : function(){
22395         var ts = this.trigger.select('.x-form-trigger', true);
22396         this.wrap.setStyle('overflow', 'hidden');
22397         var triggerField = this;
22398         ts.each(function(t, all, index){
22399             t.hide = function(){
22400                 var w = triggerField.wrap.getWidth();
22401                 this.dom.style.display = 'none';
22402                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22403             };
22404             t.show = function(){
22405                 var w = triggerField.wrap.getWidth();
22406                 this.dom.style.display = '';
22407                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22408             };
22409             var triggerIndex = 'Trigger'+(index+1);
22410
22411             if(this['hide'+triggerIndex]){
22412                 t.dom.style.display = 'none';
22413             }
22414             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22415             t.addClassOnOver('x-form-trigger-over');
22416             t.addClassOnClick('x-form-trigger-click');
22417         }, this);
22418         this.triggers = ts.elements;
22419     },
22420
22421     onTrigger1Click : Roo.emptyFn,
22422     onTrigger2Click : Roo.emptyFn
22423 });/*
22424  * Based on:
22425  * Ext JS Library 1.1.1
22426  * Copyright(c) 2006-2007, Ext JS, LLC.
22427  *
22428  * Originally Released Under LGPL - original licence link has changed is not relivant.
22429  *
22430  * Fork - LGPL
22431  * <script type="text/javascript">
22432  */
22433  
22434 /**
22435  * @class Roo.form.TextArea
22436  * @extends Roo.form.TextField
22437  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22438  * support for auto-sizing.
22439  * @constructor
22440  * Creates a new TextArea
22441  * @param {Object} config Configuration options
22442  */
22443 Roo.form.TextArea = function(config){
22444     Roo.form.TextArea.superclass.constructor.call(this, config);
22445     // these are provided exchanges for backwards compat
22446     // minHeight/maxHeight were replaced by growMin/growMax to be
22447     // compatible with TextField growing config values
22448     if(this.minHeight !== undefined){
22449         this.growMin = this.minHeight;
22450     }
22451     if(this.maxHeight !== undefined){
22452         this.growMax = this.maxHeight;
22453     }
22454 };
22455
22456 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22457     /**
22458      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22459      */
22460     growMin : 60,
22461     /**
22462      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22463      */
22464     growMax: 1000,
22465     /**
22466      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22467      * in the field (equivalent to setting overflow: hidden, defaults to false)
22468      */
22469     preventScrollbars: false,
22470     /**
22471      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22472      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22473      */
22474
22475     // private
22476     onRender : function(ct, position){
22477         if(!this.el){
22478             this.defaultAutoCreate = {
22479                 tag: "textarea",
22480                 style:"width:300px;height:60px;",
22481                 autocomplete: "off"
22482             };
22483         }
22484         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22485         if(this.grow){
22486             this.textSizeEl = Roo.DomHelper.append(document.body, {
22487                 tag: "pre", cls: "x-form-grow-sizer"
22488             });
22489             if(this.preventScrollbars){
22490                 this.el.setStyle("overflow", "hidden");
22491             }
22492             this.el.setHeight(this.growMin);
22493         }
22494     },
22495
22496     onDestroy : function(){
22497         if(this.textSizeEl){
22498             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22499         }
22500         Roo.form.TextArea.superclass.onDestroy.call(this);
22501     },
22502
22503     // private
22504     onKeyUp : function(e){
22505         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22506             this.autoSize();
22507         }
22508     },
22509
22510     /**
22511      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22512      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22513      */
22514     autoSize : function(){
22515         if(!this.grow || !this.textSizeEl){
22516             return;
22517         }
22518         var el = this.el;
22519         var v = el.dom.value;
22520         var ts = this.textSizeEl;
22521
22522         ts.innerHTML = '';
22523         ts.appendChild(document.createTextNode(v));
22524         v = ts.innerHTML;
22525
22526         Roo.fly(ts).setWidth(this.el.getWidth());
22527         if(v.length < 1){
22528             v = "&#160;&#160;";
22529         }else{
22530             if(Roo.isIE){
22531                 v = v.replace(/\n/g, '<p>&#160;</p>');
22532             }
22533             v += "&#160;\n&#160;";
22534         }
22535         ts.innerHTML = v;
22536         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22537         if(h != this.lastHeight){
22538             this.lastHeight = h;
22539             this.el.setHeight(h);
22540             this.fireEvent("autosize", this, h);
22541         }
22542     }
22543 });/*
22544  * Based on:
22545  * Ext JS Library 1.1.1
22546  * Copyright(c) 2006-2007, Ext JS, LLC.
22547  *
22548  * Originally Released Under LGPL - original licence link has changed is not relivant.
22549  *
22550  * Fork - LGPL
22551  * <script type="text/javascript">
22552  */
22553  
22554
22555 /**
22556  * @class Roo.form.NumberField
22557  * @extends Roo.form.TextField
22558  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22559  * @constructor
22560  * Creates a new NumberField
22561  * @param {Object} config Configuration options
22562  */
22563 Roo.form.NumberField = function(config){
22564     Roo.form.NumberField.superclass.constructor.call(this, config);
22565 };
22566
22567 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22568     /**
22569      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22570      */
22571     fieldClass: "x-form-field x-form-num-field",
22572     /**
22573      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22574      */
22575     allowDecimals : true,
22576     /**
22577      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22578      */
22579     decimalSeparator : ".",
22580     /**
22581      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22582      */
22583     decimalPrecision : 2,
22584     /**
22585      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22586      */
22587     allowNegative : true,
22588     /**
22589      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22590      */
22591     minValue : Number.NEGATIVE_INFINITY,
22592     /**
22593      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22594      */
22595     maxValue : Number.MAX_VALUE,
22596     /**
22597      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22598      */
22599     minText : "The minimum value for this field is {0}",
22600     /**
22601      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22602      */
22603     maxText : "The maximum value for this field is {0}",
22604     /**
22605      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22606      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22607      */
22608     nanText : "{0} is not a valid number",
22609
22610     // private
22611     initEvents : function(){
22612         Roo.form.NumberField.superclass.initEvents.call(this);
22613         var allowed = "0123456789";
22614         if(this.allowDecimals){
22615             allowed += this.decimalSeparator;
22616         }
22617         if(this.allowNegative){
22618             allowed += "-";
22619         }
22620         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22621         var keyPress = function(e){
22622             var k = e.getKey();
22623             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22624                 return;
22625             }
22626             var c = e.getCharCode();
22627             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22628                 e.stopEvent();
22629             }
22630         };
22631         this.el.on("keypress", keyPress, this);
22632     },
22633
22634     // private
22635     validateValue : function(value){
22636         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22637             return false;
22638         }
22639         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22640              return true;
22641         }
22642         var num = this.parseValue(value);
22643         if(isNaN(num)){
22644             this.markInvalid(String.format(this.nanText, value));
22645             return false;
22646         }
22647         if(num < this.minValue){
22648             this.markInvalid(String.format(this.minText, this.minValue));
22649             return false;
22650         }
22651         if(num > this.maxValue){
22652             this.markInvalid(String.format(this.maxText, this.maxValue));
22653             return false;
22654         }
22655         return true;
22656     },
22657
22658     getValue : function(){
22659         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22660     },
22661
22662     // private
22663     parseValue : function(value){
22664         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22665         return isNaN(value) ? '' : value;
22666     },
22667
22668     // private
22669     fixPrecision : function(value){
22670         var nan = isNaN(value);
22671         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22672             return nan ? '' : value;
22673         }
22674         return parseFloat(value).toFixed(this.decimalPrecision);
22675     },
22676
22677     setValue : function(v){
22678         v = this.fixPrecision(v);
22679         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22680     },
22681
22682     // private
22683     decimalPrecisionFcn : function(v){
22684         return Math.floor(v);
22685     },
22686
22687     beforeBlur : function(){
22688         var v = this.parseValue(this.getRawValue());
22689         if(v){
22690             this.setValue(v);
22691         }
22692     }
22693 });/*
22694  * Based on:
22695  * Ext JS Library 1.1.1
22696  * Copyright(c) 2006-2007, Ext JS, LLC.
22697  *
22698  * Originally Released Under LGPL - original licence link has changed is not relivant.
22699  *
22700  * Fork - LGPL
22701  * <script type="text/javascript">
22702  */
22703  
22704 /**
22705  * @class Roo.form.DateField
22706  * @extends Roo.form.TriggerField
22707  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22708 * @constructor
22709 * Create a new DateField
22710 * @param {Object} config
22711  */
22712 Roo.form.DateField = function(config){
22713     Roo.form.DateField.superclass.constructor.call(this, config);
22714     
22715       this.addEvents({
22716          
22717         /**
22718          * @event select
22719          * Fires when a date is selected
22720              * @param {Roo.form.DateField} combo This combo box
22721              * @param {Date} date The date selected
22722              */
22723         'select' : true
22724          
22725     });
22726     
22727     
22728     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22729     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22730     this.ddMatch = null;
22731     if(this.disabledDates){
22732         var dd = this.disabledDates;
22733         var re = "(?:";
22734         for(var i = 0; i < dd.length; i++){
22735             re += dd[i];
22736             if(i != dd.length-1) re += "|";
22737         }
22738         this.ddMatch = new RegExp(re + ")");
22739     }
22740 };
22741
22742 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22743     /**
22744      * @cfg {String} format
22745      * The default date format string which can be overriden for localization support.  The format must be
22746      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22747      */
22748     format : "m/d/y",
22749     /**
22750      * @cfg {String} altFormats
22751      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22752      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22753      */
22754     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22755     /**
22756      * @cfg {Array} disabledDays
22757      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22758      */
22759     disabledDays : null,
22760     /**
22761      * @cfg {String} disabledDaysText
22762      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22763      */
22764     disabledDaysText : "Disabled",
22765     /**
22766      * @cfg {Array} disabledDates
22767      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22768      * expression so they are very powerful. Some examples:
22769      * <ul>
22770      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22771      * <li>["03/08", "09/16"] would disable those days for every year</li>
22772      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22773      * <li>["03/../2006"] would disable every day in March 2006</li>
22774      * <li>["^03"] would disable every day in every March</li>
22775      * </ul>
22776      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22777      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22778      */
22779     disabledDates : null,
22780     /**
22781      * @cfg {String} disabledDatesText
22782      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22783      */
22784     disabledDatesText : "Disabled",
22785     /**
22786      * @cfg {Date/String} minValue
22787      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22788      * valid format (defaults to null).
22789      */
22790     minValue : null,
22791     /**
22792      * @cfg {Date/String} maxValue
22793      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22794      * valid format (defaults to null).
22795      */
22796     maxValue : null,
22797     /**
22798      * @cfg {String} minText
22799      * The error text to display when the date in the cell is before minValue (defaults to
22800      * 'The date in this field must be after {minValue}').
22801      */
22802     minText : "The date in this field must be equal to or after {0}",
22803     /**
22804      * @cfg {String} maxText
22805      * The error text to display when the date in the cell is after maxValue (defaults to
22806      * 'The date in this field must be before {maxValue}').
22807      */
22808     maxText : "The date in this field must be equal to or before {0}",
22809     /**
22810      * @cfg {String} invalidText
22811      * The error text to display when the date in the field is invalid (defaults to
22812      * '{value} is not a valid date - it must be in the format {format}').
22813      */
22814     invalidText : "{0} is not a valid date - it must be in the format {1}",
22815     /**
22816      * @cfg {String} triggerClass
22817      * An additional CSS class used to style the trigger button.  The trigger will always get the
22818      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22819      * which displays a calendar icon).
22820      */
22821     triggerClass : 'x-form-date-trigger',
22822     
22823
22824     /**
22825      * @cfg {Boolean} useIso
22826      * if enabled, then the date field will use a hidden field to store the 
22827      * real value as iso formated date. default (false)
22828      */ 
22829     useIso : false,
22830     /**
22831      * @cfg {String/Object} autoCreate
22832      * A DomHelper element spec, or true for a default element spec (defaults to
22833      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22834      */ 
22835     // private
22836     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22837     
22838     // private
22839     hiddenField: false,
22840     
22841     onRender : function(ct, position)
22842     {
22843         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22844         if (this.useIso) {
22845             //this.el.dom.removeAttribute('name'); 
22846             Roo.log("Changing name?");
22847             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22848             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22849                     'before', true);
22850             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22851             // prevent input submission
22852             this.hiddenName = this.name;
22853         }
22854             
22855             
22856     },
22857     
22858     // private
22859     validateValue : function(value)
22860     {
22861         value = this.formatDate(value);
22862         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22863             Roo.log('super failed');
22864             return false;
22865         }
22866         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22867              return true;
22868         }
22869         var svalue = value;
22870         value = this.parseDate(value);
22871         if(!value){
22872             Roo.log('parse date failed' + svalue);
22873             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22874             return false;
22875         }
22876         var time = value.getTime();
22877         if(this.minValue && time < this.minValue.getTime()){
22878             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22879             return false;
22880         }
22881         if(this.maxValue && time > this.maxValue.getTime()){
22882             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22883             return false;
22884         }
22885         if(this.disabledDays){
22886             var day = value.getDay();
22887             for(var i = 0; i < this.disabledDays.length; i++) {
22888                 if(day === this.disabledDays[i]){
22889                     this.markInvalid(this.disabledDaysText);
22890                     return false;
22891                 }
22892             }
22893         }
22894         var fvalue = this.formatDate(value);
22895         if(this.ddMatch && this.ddMatch.test(fvalue)){
22896             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22897             return false;
22898         }
22899         return true;
22900     },
22901
22902     // private
22903     // Provides logic to override the default TriggerField.validateBlur which just returns true
22904     validateBlur : function(){
22905         return !this.menu || !this.menu.isVisible();
22906     },
22907     
22908     getName: function()
22909     {
22910         // returns hidden if it's set..
22911         if (!this.rendered) {return ''};
22912         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22913         
22914     },
22915
22916     /**
22917      * Returns the current date value of the date field.
22918      * @return {Date} The date value
22919      */
22920     getValue : function(){
22921         
22922         return  this.hiddenField ?
22923                 this.hiddenField.value :
22924                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22925     },
22926
22927     /**
22928      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22929      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22930      * (the default format used is "m/d/y").
22931      * <br />Usage:
22932      * <pre><code>
22933 //All of these calls set the same date value (May 4, 2006)
22934
22935 //Pass a date object:
22936 var dt = new Date('5/4/06');
22937 dateField.setValue(dt);
22938
22939 //Pass a date string (default format):
22940 dateField.setValue('5/4/06');
22941
22942 //Pass a date string (custom format):
22943 dateField.format = 'Y-m-d';
22944 dateField.setValue('2006-5-4');
22945 </code></pre>
22946      * @param {String/Date} date The date or valid date string
22947      */
22948     setValue : function(date){
22949         if (this.hiddenField) {
22950             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22951         }
22952         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22953         // make sure the value field is always stored as a date..
22954         this.value = this.parseDate(date);
22955         
22956         
22957     },
22958
22959     // private
22960     parseDate : function(value){
22961         if(!value || value instanceof Date){
22962             return value;
22963         }
22964         var v = Date.parseDate(value, this.format);
22965          if (!v && this.useIso) {
22966             v = Date.parseDate(value, 'Y-m-d');
22967         }
22968         if(!v && this.altFormats){
22969             if(!this.altFormatsArray){
22970                 this.altFormatsArray = this.altFormats.split("|");
22971             }
22972             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22973                 v = Date.parseDate(value, this.altFormatsArray[i]);
22974             }
22975         }
22976         return v;
22977     },
22978
22979     // private
22980     formatDate : function(date, fmt){
22981         return (!date || !(date instanceof Date)) ?
22982                date : date.dateFormat(fmt || this.format);
22983     },
22984
22985     // private
22986     menuListeners : {
22987         select: function(m, d){
22988             
22989             this.setValue(d);
22990             this.fireEvent('select', this, d);
22991         },
22992         show : function(){ // retain focus styling
22993             this.onFocus();
22994         },
22995         hide : function(){
22996             this.focus.defer(10, this);
22997             var ml = this.menuListeners;
22998             this.menu.un("select", ml.select,  this);
22999             this.menu.un("show", ml.show,  this);
23000             this.menu.un("hide", ml.hide,  this);
23001         }
23002     },
23003
23004     // private
23005     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23006     onTriggerClick : function(){
23007         if(this.disabled){
23008             return;
23009         }
23010         if(this.menu == null){
23011             this.menu = new Roo.menu.DateMenu();
23012         }
23013         Roo.apply(this.menu.picker,  {
23014             showClear: this.allowBlank,
23015             minDate : this.minValue,
23016             maxDate : this.maxValue,
23017             disabledDatesRE : this.ddMatch,
23018             disabledDatesText : this.disabledDatesText,
23019             disabledDays : this.disabledDays,
23020             disabledDaysText : this.disabledDaysText,
23021             format : this.useIso ? 'Y-m-d' : this.format,
23022             minText : String.format(this.minText, this.formatDate(this.minValue)),
23023             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23024         });
23025         this.menu.on(Roo.apply({}, this.menuListeners, {
23026             scope:this
23027         }));
23028         this.menu.picker.setValue(this.getValue() || new Date());
23029         this.menu.show(this.el, "tl-bl?");
23030     },
23031
23032     beforeBlur : function(){
23033         var v = this.parseDate(this.getRawValue());
23034         if(v){
23035             this.setValue(v);
23036         }
23037     }
23038
23039     /** @cfg {Boolean} grow @hide */
23040     /** @cfg {Number} growMin @hide */
23041     /** @cfg {Number} growMax @hide */
23042     /**
23043      * @hide
23044      * @method autoSize
23045      */
23046 });/*
23047  * Based on:
23048  * Ext JS Library 1.1.1
23049  * Copyright(c) 2006-2007, Ext JS, LLC.
23050  *
23051  * Originally Released Under LGPL - original licence link has changed is not relivant.
23052  *
23053  * Fork - LGPL
23054  * <script type="text/javascript">
23055  */
23056  
23057 /**
23058  * @class Roo.form.MonthField
23059  * @extends Roo.form.TriggerField
23060  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23061 * @constructor
23062 * Create a new MonthField
23063 * @param {Object} config
23064  */
23065 Roo.form.MonthField = function(config){
23066     
23067     Roo.form.MonthField.superclass.constructor.call(this, config);
23068     
23069       this.addEvents({
23070          
23071         /**
23072          * @event select
23073          * Fires when a date is selected
23074              * @param {Roo.form.MonthFieeld} combo This combo box
23075              * @param {Date} date The date selected
23076              */
23077         'select' : true
23078          
23079     });
23080     
23081     
23082     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23083     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23084     this.ddMatch = null;
23085     if(this.disabledDates){
23086         var dd = this.disabledDates;
23087         var re = "(?:";
23088         for(var i = 0; i < dd.length; i++){
23089             re += dd[i];
23090             if(i != dd.length-1) re += "|";
23091         }
23092         this.ddMatch = new RegExp(re + ")");
23093     }
23094 };
23095
23096 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23097     /**
23098      * @cfg {String} format
23099      * The default date format string which can be overriden for localization support.  The format must be
23100      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23101      */
23102     format : "M Y",
23103     /**
23104      * @cfg {String} altFormats
23105      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23106      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23107      */
23108     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23109     /**
23110      * @cfg {Array} disabledDays
23111      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23112      */
23113     disabledDays : [0,1,2,3,4,5,6],
23114     /**
23115      * @cfg {String} disabledDaysText
23116      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23117      */
23118     disabledDaysText : "Disabled",
23119     /**
23120      * @cfg {Array} disabledDates
23121      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23122      * expression so they are very powerful. Some examples:
23123      * <ul>
23124      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23125      * <li>["03/08", "09/16"] would disable those days for every year</li>
23126      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23127      * <li>["03/../2006"] would disable every day in March 2006</li>
23128      * <li>["^03"] would disable every day in every March</li>
23129      * </ul>
23130      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23131      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23132      */
23133     disabledDates : null,
23134     /**
23135      * @cfg {String} disabledDatesText
23136      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23137      */
23138     disabledDatesText : "Disabled",
23139     /**
23140      * @cfg {Date/String} minValue
23141      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23142      * valid format (defaults to null).
23143      */
23144     minValue : null,
23145     /**
23146      * @cfg {Date/String} maxValue
23147      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23148      * valid format (defaults to null).
23149      */
23150     maxValue : null,
23151     /**
23152      * @cfg {String} minText
23153      * The error text to display when the date in the cell is before minValue (defaults to
23154      * 'The date in this field must be after {minValue}').
23155      */
23156     minText : "The date in this field must be equal to or after {0}",
23157     /**
23158      * @cfg {String} maxTextf
23159      * The error text to display when the date in the cell is after maxValue (defaults to
23160      * 'The date in this field must be before {maxValue}').
23161      */
23162     maxText : "The date in this field must be equal to or before {0}",
23163     /**
23164      * @cfg {String} invalidText
23165      * The error text to display when the date in the field is invalid (defaults to
23166      * '{value} is not a valid date - it must be in the format {format}').
23167      */
23168     invalidText : "{0} is not a valid date - it must be in the format {1}",
23169     /**
23170      * @cfg {String} triggerClass
23171      * An additional CSS class used to style the trigger button.  The trigger will always get the
23172      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23173      * which displays a calendar icon).
23174      */
23175     triggerClass : 'x-form-date-trigger',
23176     
23177
23178     /**
23179      * @cfg {Boolean} useIso
23180      * if enabled, then the date field will use a hidden field to store the 
23181      * real value as iso formated date. default (true)
23182      */ 
23183     useIso : true,
23184     /**
23185      * @cfg {String/Object} autoCreate
23186      * A DomHelper element spec, or true for a default element spec (defaults to
23187      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23188      */ 
23189     // private
23190     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23191     
23192     // private
23193     hiddenField: false,
23194     
23195     hideMonthPicker : false,
23196     
23197     onRender : function(ct, position)
23198     {
23199         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23200         if (this.useIso) {
23201             this.el.dom.removeAttribute('name'); 
23202             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23203                     'before', true);
23204             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23205             // prevent input submission
23206             this.hiddenName = this.name;
23207         }
23208             
23209             
23210     },
23211     
23212     // private
23213     validateValue : function(value)
23214     {
23215         value = this.formatDate(value);
23216         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23217             return false;
23218         }
23219         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23220              return true;
23221         }
23222         var svalue = value;
23223         value = this.parseDate(value);
23224         if(!value){
23225             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23226             return false;
23227         }
23228         var time = value.getTime();
23229         if(this.minValue && time < this.minValue.getTime()){
23230             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23231             return false;
23232         }
23233         if(this.maxValue && time > this.maxValue.getTime()){
23234             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23235             return false;
23236         }
23237         /*if(this.disabledDays){
23238             var day = value.getDay();
23239             for(var i = 0; i < this.disabledDays.length; i++) {
23240                 if(day === this.disabledDays[i]){
23241                     this.markInvalid(this.disabledDaysText);
23242                     return false;
23243                 }
23244             }
23245         }
23246         */
23247         var fvalue = this.formatDate(value);
23248         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23249             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23250             return false;
23251         }
23252         */
23253         return true;
23254     },
23255
23256     // private
23257     // Provides logic to override the default TriggerField.validateBlur which just returns true
23258     validateBlur : function(){
23259         return !this.menu || !this.menu.isVisible();
23260     },
23261
23262     /**
23263      * Returns the current date value of the date field.
23264      * @return {Date} The date value
23265      */
23266     getValue : function(){
23267         
23268         
23269         
23270         return  this.hiddenField ?
23271                 this.hiddenField.value :
23272                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23273     },
23274
23275     /**
23276      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23277      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23278      * (the default format used is "m/d/y").
23279      * <br />Usage:
23280      * <pre><code>
23281 //All of these calls set the same date value (May 4, 2006)
23282
23283 //Pass a date object:
23284 var dt = new Date('5/4/06');
23285 monthField.setValue(dt);
23286
23287 //Pass a date string (default format):
23288 monthField.setValue('5/4/06');
23289
23290 //Pass a date string (custom format):
23291 monthField.format = 'Y-m-d';
23292 monthField.setValue('2006-5-4');
23293 </code></pre>
23294      * @param {String/Date} date The date or valid date string
23295      */
23296     setValue : function(date){
23297         Roo.log('month setValue' + date);
23298         // can only be first of month..
23299         
23300         var val = this.parseDate(date);
23301         
23302         if (this.hiddenField) {
23303             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23304         }
23305         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23306         this.value = this.parseDate(date);
23307     },
23308
23309     // private
23310     parseDate : function(value){
23311         if(!value || value instanceof Date){
23312             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23313             return value;
23314         }
23315         var v = Date.parseDate(value, this.format);
23316         if (!v && this.useIso) {
23317             v = Date.parseDate(value, 'Y-m-d');
23318         }
23319         if (v) {
23320             // 
23321             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23322         }
23323         
23324         
23325         if(!v && this.altFormats){
23326             if(!this.altFormatsArray){
23327                 this.altFormatsArray = this.altFormats.split("|");
23328             }
23329             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23330                 v = Date.parseDate(value, this.altFormatsArray[i]);
23331             }
23332         }
23333         return v;
23334     },
23335
23336     // private
23337     formatDate : function(date, fmt){
23338         return (!date || !(date instanceof Date)) ?
23339                date : date.dateFormat(fmt || this.format);
23340     },
23341
23342     // private
23343     menuListeners : {
23344         select: function(m, d){
23345             this.setValue(d);
23346             this.fireEvent('select', this, d);
23347         },
23348         show : function(){ // retain focus styling
23349             this.onFocus();
23350         },
23351         hide : function(){
23352             this.focus.defer(10, this);
23353             var ml = this.menuListeners;
23354             this.menu.un("select", ml.select,  this);
23355             this.menu.un("show", ml.show,  this);
23356             this.menu.un("hide", ml.hide,  this);
23357         }
23358     },
23359     // private
23360     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23361     onTriggerClick : function(){
23362         if(this.disabled){
23363             return;
23364         }
23365         if(this.menu == null){
23366             this.menu = new Roo.menu.DateMenu();
23367            
23368         }
23369         
23370         Roo.apply(this.menu.picker,  {
23371             
23372             showClear: this.allowBlank,
23373             minDate : this.minValue,
23374             maxDate : this.maxValue,
23375             disabledDatesRE : this.ddMatch,
23376             disabledDatesText : this.disabledDatesText,
23377             
23378             format : this.useIso ? 'Y-m-d' : this.format,
23379             minText : String.format(this.minText, this.formatDate(this.minValue)),
23380             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23381             
23382         });
23383          this.menu.on(Roo.apply({}, this.menuListeners, {
23384             scope:this
23385         }));
23386        
23387         
23388         var m = this.menu;
23389         var p = m.picker;
23390         
23391         // hide month picker get's called when we called by 'before hide';
23392         
23393         var ignorehide = true;
23394         p.hideMonthPicker  = function(disableAnim){
23395             if (ignorehide) {
23396                 return;
23397             }
23398              if(this.monthPicker){
23399                 Roo.log("hideMonthPicker called");
23400                 if(disableAnim === true){
23401                     this.monthPicker.hide();
23402                 }else{
23403                     this.monthPicker.slideOut('t', {duration:.2});
23404                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23405                     p.fireEvent("select", this, this.value);
23406                     m.hide();
23407                 }
23408             }
23409         }
23410         
23411         Roo.log('picker set value');
23412         Roo.log(this.getValue());
23413         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23414         m.show(this.el, 'tl-bl?');
23415         ignorehide  = false;
23416         // this will trigger hideMonthPicker..
23417         
23418         
23419         // hidden the day picker
23420         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23421         
23422         
23423         
23424       
23425         
23426         p.showMonthPicker.defer(100, p);
23427     
23428         
23429        
23430     },
23431
23432     beforeBlur : function(){
23433         var v = this.parseDate(this.getRawValue());
23434         if(v){
23435             this.setValue(v);
23436         }
23437     }
23438
23439     /** @cfg {Boolean} grow @hide */
23440     /** @cfg {Number} growMin @hide */
23441     /** @cfg {Number} growMax @hide */
23442     /**
23443      * @hide
23444      * @method autoSize
23445      */
23446 });/*
23447  * Based on:
23448  * Ext JS Library 1.1.1
23449  * Copyright(c) 2006-2007, Ext JS, LLC.
23450  *
23451  * Originally Released Under LGPL - original licence link has changed is not relivant.
23452  *
23453  * Fork - LGPL
23454  * <script type="text/javascript">
23455  */
23456  
23457
23458 /**
23459  * @class Roo.form.ComboBox
23460  * @extends Roo.form.TriggerField
23461  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23462  * @constructor
23463  * Create a new ComboBox.
23464  * @param {Object} config Configuration options
23465  */
23466 Roo.form.ComboBox = function(config){
23467     Roo.form.ComboBox.superclass.constructor.call(this, config);
23468     this.addEvents({
23469         /**
23470          * @event expand
23471          * Fires when the dropdown list is expanded
23472              * @param {Roo.form.ComboBox} combo This combo box
23473              */
23474         'expand' : true,
23475         /**
23476          * @event collapse
23477          * Fires when the dropdown list is collapsed
23478              * @param {Roo.form.ComboBox} combo This combo box
23479              */
23480         'collapse' : true,
23481         /**
23482          * @event beforeselect
23483          * Fires before a list item is selected. Return false to cancel the selection.
23484              * @param {Roo.form.ComboBox} combo This combo box
23485              * @param {Roo.data.Record} record The data record returned from the underlying store
23486              * @param {Number} index The index of the selected item in the dropdown list
23487              */
23488         'beforeselect' : true,
23489         /**
23490          * @event select
23491          * Fires when a list item is selected
23492              * @param {Roo.form.ComboBox} combo This combo box
23493              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23494              * @param {Number} index The index of the selected item in the dropdown list
23495              */
23496         'select' : true,
23497         /**
23498          * @event beforequery
23499          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23500          * The event object passed has these properties:
23501              * @param {Roo.form.ComboBox} combo This combo box
23502              * @param {String} query The query
23503              * @param {Boolean} forceAll true to force "all" query
23504              * @param {Boolean} cancel true to cancel the query
23505              * @param {Object} e The query event object
23506              */
23507         'beforequery': true,
23508          /**
23509          * @event add
23510          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23511              * @param {Roo.form.ComboBox} combo This combo box
23512              */
23513         'add' : true,
23514         /**
23515          * @event edit
23516          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23517              * @param {Roo.form.ComboBox} combo This combo box
23518              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23519              */
23520         'edit' : true
23521         
23522         
23523     });
23524     if(this.transform){
23525         this.allowDomMove = false;
23526         var s = Roo.getDom(this.transform);
23527         if(!this.hiddenName){
23528             this.hiddenName = s.name;
23529         }
23530         if(!this.store){
23531             this.mode = 'local';
23532             var d = [], opts = s.options;
23533             for(var i = 0, len = opts.length;i < len; i++){
23534                 var o = opts[i];
23535                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23536                 if(o.selected) {
23537                     this.value = value;
23538                 }
23539                 d.push([value, o.text]);
23540             }
23541             this.store = new Roo.data.SimpleStore({
23542                 'id': 0,
23543                 fields: ['value', 'text'],
23544                 data : d
23545             });
23546             this.valueField = 'value';
23547             this.displayField = 'text';
23548         }
23549         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23550         if(!this.lazyRender){
23551             this.target = true;
23552             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23553             s.parentNode.removeChild(s); // remove it
23554             this.render(this.el.parentNode);
23555         }else{
23556             s.parentNode.removeChild(s); // remove it
23557         }
23558
23559     }
23560     if (this.store) {
23561         this.store = Roo.factory(this.store, Roo.data);
23562     }
23563     
23564     this.selectedIndex = -1;
23565     if(this.mode == 'local'){
23566         if(config.queryDelay === undefined){
23567             this.queryDelay = 10;
23568         }
23569         if(config.minChars === undefined){
23570             this.minChars = 0;
23571         }
23572     }
23573 };
23574
23575 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23576     /**
23577      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23578      */
23579     /**
23580      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23581      * rendering into an Roo.Editor, defaults to false)
23582      */
23583     /**
23584      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23585      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23586      */
23587     /**
23588      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23589      */
23590     /**
23591      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23592      * the dropdown list (defaults to undefined, with no header element)
23593      */
23594
23595      /**
23596      * @cfg {String/Roo.Template} tpl The template to use to render the output
23597      */
23598      
23599     // private
23600     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23601     /**
23602      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23603      */
23604     listWidth: undefined,
23605     /**
23606      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23607      * mode = 'remote' or 'text' if mode = 'local')
23608      */
23609     displayField: undefined,
23610     /**
23611      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23612      * mode = 'remote' or 'value' if mode = 'local'). 
23613      * Note: use of a valueField requires the user make a selection
23614      * in order for a value to be mapped.
23615      */
23616     valueField: undefined,
23617     
23618     
23619     /**
23620      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23621      * field's data value (defaults to the underlying DOM element's name)
23622      */
23623     hiddenName: undefined,
23624     /**
23625      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23626      */
23627     listClass: '',
23628     /**
23629      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23630      */
23631     selectedClass: 'x-combo-selected',
23632     /**
23633      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23634      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23635      * which displays a downward arrow icon).
23636      */
23637     triggerClass : 'x-form-arrow-trigger',
23638     /**
23639      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23640      */
23641     shadow:'sides',
23642     /**
23643      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23644      * anchor positions (defaults to 'tl-bl')
23645      */
23646     listAlign: 'tl-bl?',
23647     /**
23648      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23649      */
23650     maxHeight: 300,
23651     /**
23652      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23653      * query specified by the allQuery config option (defaults to 'query')
23654      */
23655     triggerAction: 'query',
23656     /**
23657      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23658      * (defaults to 4, does not apply if editable = false)
23659      */
23660     minChars : 4,
23661     /**
23662      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23663      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23664      */
23665     typeAhead: false,
23666     /**
23667      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23668      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23669      */
23670     queryDelay: 500,
23671     /**
23672      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23673      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23674      */
23675     pageSize: 0,
23676     /**
23677      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23678      * when editable = true (defaults to false)
23679      */
23680     selectOnFocus:false,
23681     /**
23682      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23683      */
23684     queryParam: 'query',
23685     /**
23686      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23687      * when mode = 'remote' (defaults to 'Loading...')
23688      */
23689     loadingText: 'Loading...',
23690     /**
23691      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23692      */
23693     resizable: false,
23694     /**
23695      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23696      */
23697     handleHeight : 8,
23698     /**
23699      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23700      * traditional select (defaults to true)
23701      */
23702     editable: true,
23703     /**
23704      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23705      */
23706     allQuery: '',
23707     /**
23708      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23709      */
23710     mode: 'remote',
23711     /**
23712      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23713      * listWidth has a higher value)
23714      */
23715     minListWidth : 70,
23716     /**
23717      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23718      * allow the user to set arbitrary text into the field (defaults to false)
23719      */
23720     forceSelection:false,
23721     /**
23722      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23723      * if typeAhead = true (defaults to 250)
23724      */
23725     typeAheadDelay : 250,
23726     /**
23727      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23728      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23729      */
23730     valueNotFoundText : undefined,
23731     /**
23732      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23733      */
23734     blockFocus : false,
23735     
23736     /**
23737      * @cfg {Boolean} disableClear Disable showing of clear button.
23738      */
23739     disableClear : false,
23740     /**
23741      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23742      */
23743     alwaysQuery : false,
23744     
23745     //private
23746     addicon : false,
23747     editicon: false,
23748     
23749     // element that contains real text value.. (when hidden is used..)
23750      
23751     // private
23752     onRender : function(ct, position){
23753         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23754         if(this.hiddenName){
23755             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23756                     'before', true);
23757             this.hiddenField.value =
23758                 this.hiddenValue !== undefined ? this.hiddenValue :
23759                 this.value !== undefined ? this.value : '';
23760
23761             // prevent input submission
23762             this.el.dom.removeAttribute('name');
23763              
23764              
23765         }
23766         if(Roo.isGecko){
23767             this.el.dom.setAttribute('autocomplete', 'off');
23768         }
23769
23770         var cls = 'x-combo-list';
23771
23772         this.list = new Roo.Layer({
23773             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23774         });
23775
23776         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23777         this.list.setWidth(lw);
23778         this.list.swallowEvent('mousewheel');
23779         this.assetHeight = 0;
23780
23781         if(this.title){
23782             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23783             this.assetHeight += this.header.getHeight();
23784         }
23785
23786         this.innerList = this.list.createChild({cls:cls+'-inner'});
23787         this.innerList.on('mouseover', this.onViewOver, this);
23788         this.innerList.on('mousemove', this.onViewMove, this);
23789         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23790         
23791         if(this.allowBlank && !this.pageSize && !this.disableClear){
23792             this.footer = this.list.createChild({cls:cls+'-ft'});
23793             this.pageTb = new Roo.Toolbar(this.footer);
23794            
23795         }
23796         if(this.pageSize){
23797             this.footer = this.list.createChild({cls:cls+'-ft'});
23798             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23799                     {pageSize: this.pageSize});
23800             
23801         }
23802         
23803         if (this.pageTb && this.allowBlank && !this.disableClear) {
23804             var _this = this;
23805             this.pageTb.add(new Roo.Toolbar.Fill(), {
23806                 cls: 'x-btn-icon x-btn-clear',
23807                 text: '&#160;',
23808                 handler: function()
23809                 {
23810                     _this.collapse();
23811                     _this.clearValue();
23812                     _this.onSelect(false, -1);
23813                 }
23814             });
23815         }
23816         if (this.footer) {
23817             this.assetHeight += this.footer.getHeight();
23818         }
23819         
23820
23821         if(!this.tpl){
23822             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23823         }
23824
23825         this.view = new Roo.View(this.innerList, this.tpl, {
23826             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23827         });
23828
23829         this.view.on('click', this.onViewClick, this);
23830
23831         this.store.on('beforeload', this.onBeforeLoad, this);
23832         this.store.on('load', this.onLoad, this);
23833         this.store.on('loadexception', this.onLoadException, this);
23834
23835         if(this.resizable){
23836             this.resizer = new Roo.Resizable(this.list,  {
23837                pinned:true, handles:'se'
23838             });
23839             this.resizer.on('resize', function(r, w, h){
23840                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23841                 this.listWidth = w;
23842                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23843                 this.restrictHeight();
23844             }, this);
23845             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23846         }
23847         if(!this.editable){
23848             this.editable = true;
23849             this.setEditable(false);
23850         }  
23851         
23852         
23853         if (typeof(this.events.add.listeners) != 'undefined') {
23854             
23855             this.addicon = this.wrap.createChild(
23856                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23857        
23858             this.addicon.on('click', function(e) {
23859                 this.fireEvent('add', this);
23860             }, this);
23861         }
23862         if (typeof(this.events.edit.listeners) != 'undefined') {
23863             
23864             this.editicon = this.wrap.createChild(
23865                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23866             if (this.addicon) {
23867                 this.editicon.setStyle('margin-left', '40px');
23868             }
23869             this.editicon.on('click', function(e) {
23870                 
23871                 // we fire even  if inothing is selected..
23872                 this.fireEvent('edit', this, this.lastData );
23873                 
23874             }, this);
23875         }
23876         
23877         
23878         
23879     },
23880
23881     // private
23882     initEvents : function(){
23883         Roo.form.ComboBox.superclass.initEvents.call(this);
23884
23885         this.keyNav = new Roo.KeyNav(this.el, {
23886             "up" : function(e){
23887                 this.inKeyMode = true;
23888                 this.selectPrev();
23889             },
23890
23891             "down" : function(e){
23892                 if(!this.isExpanded()){
23893                     this.onTriggerClick();
23894                 }else{
23895                     this.inKeyMode = true;
23896                     this.selectNext();
23897                 }
23898             },
23899
23900             "enter" : function(e){
23901                 this.onViewClick();
23902                 //return true;
23903             },
23904
23905             "esc" : function(e){
23906                 this.collapse();
23907             },
23908
23909             "tab" : function(e){
23910                 this.onViewClick(false);
23911                 this.fireEvent("specialkey", this, e);
23912                 return true;
23913             },
23914
23915             scope : this,
23916
23917             doRelay : function(foo, bar, hname){
23918                 if(hname == 'down' || this.scope.isExpanded()){
23919                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23920                 }
23921                 return true;
23922             },
23923
23924             forceKeyDown: true
23925         });
23926         this.queryDelay = Math.max(this.queryDelay || 10,
23927                 this.mode == 'local' ? 10 : 250);
23928         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23929         if(this.typeAhead){
23930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23931         }
23932         if(this.editable !== false){
23933             this.el.on("keyup", this.onKeyUp, this);
23934         }
23935         if(this.forceSelection){
23936             this.on('blur', this.doForce, this);
23937         }
23938     },
23939
23940     onDestroy : function(){
23941         if(this.view){
23942             this.view.setStore(null);
23943             this.view.el.removeAllListeners();
23944             this.view.el.remove();
23945             this.view.purgeListeners();
23946         }
23947         if(this.list){
23948             this.list.destroy();
23949         }
23950         if(this.store){
23951             this.store.un('beforeload', this.onBeforeLoad, this);
23952             this.store.un('load', this.onLoad, this);
23953             this.store.un('loadexception', this.onLoadException, this);
23954         }
23955         Roo.form.ComboBox.superclass.onDestroy.call(this);
23956     },
23957
23958     // private
23959     fireKey : function(e){
23960         if(e.isNavKeyPress() && !this.list.isVisible()){
23961             this.fireEvent("specialkey", this, e);
23962         }
23963     },
23964
23965     // private
23966     onResize: function(w, h){
23967         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23968         
23969         if(typeof w != 'number'){
23970             // we do not handle it!?!?
23971             return;
23972         }
23973         var tw = this.trigger.getWidth();
23974         tw += this.addicon ? this.addicon.getWidth() : 0;
23975         tw += this.editicon ? this.editicon.getWidth() : 0;
23976         var x = w - tw;
23977         this.el.setWidth( this.adjustWidth('input', x));
23978             
23979         this.trigger.setStyle('left', x+'px');
23980         
23981         if(this.list && this.listWidth === undefined){
23982             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23983             this.list.setWidth(lw);
23984             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23985         }
23986         
23987     
23988         
23989     },
23990
23991     /**
23992      * Allow or prevent the user from directly editing the field text.  If false is passed,
23993      * the user will only be able to select from the items defined in the dropdown list.  This method
23994      * is the runtime equivalent of setting the 'editable' config option at config time.
23995      * @param {Boolean} value True to allow the user to directly edit the field text
23996      */
23997     setEditable : function(value){
23998         if(value == this.editable){
23999             return;
24000         }
24001         this.editable = value;
24002         if(!value){
24003             this.el.dom.setAttribute('readOnly', true);
24004             this.el.on('mousedown', this.onTriggerClick,  this);
24005             this.el.addClass('x-combo-noedit');
24006         }else{
24007             this.el.dom.setAttribute('readOnly', false);
24008             this.el.un('mousedown', this.onTriggerClick,  this);
24009             this.el.removeClass('x-combo-noedit');
24010         }
24011     },
24012
24013     // private
24014     onBeforeLoad : function(){
24015         if(!this.hasFocus){
24016             return;
24017         }
24018         this.innerList.update(this.loadingText ?
24019                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24020         this.restrictHeight();
24021         this.selectedIndex = -1;
24022     },
24023
24024     // private
24025     onLoad : function(){
24026         if(!this.hasFocus){
24027             return;
24028         }
24029         if(this.store.getCount() > 0){
24030             this.expand();
24031             this.restrictHeight();
24032             if(this.lastQuery == this.allQuery){
24033                 if(this.editable){
24034                     this.el.dom.select();
24035                 }
24036                 if(!this.selectByValue(this.value, true)){
24037                     this.select(0, true);
24038                 }
24039             }else{
24040                 this.selectNext();
24041                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24042                     this.taTask.delay(this.typeAheadDelay);
24043                 }
24044             }
24045         }else{
24046             this.onEmptyResults();
24047         }
24048         //this.el.focus();
24049     },
24050     // private
24051     onLoadException : function()
24052     {
24053         this.collapse();
24054         Roo.log(this.store.reader.jsonData);
24055         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24056             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24057         }
24058         
24059         
24060     },
24061     // private
24062     onTypeAhead : function(){
24063         if(this.store.getCount() > 0){
24064             var r = this.store.getAt(0);
24065             var newValue = r.data[this.displayField];
24066             var len = newValue.length;
24067             var selStart = this.getRawValue().length;
24068             if(selStart != len){
24069                 this.setRawValue(newValue);
24070                 this.selectText(selStart, newValue.length);
24071             }
24072         }
24073     },
24074
24075     // private
24076     onSelect : function(record, index){
24077         if(this.fireEvent('beforeselect', this, record, index) !== false){
24078             this.setFromData(index > -1 ? record.data : false);
24079             this.collapse();
24080             this.fireEvent('select', this, record, index);
24081         }
24082     },
24083
24084     /**
24085      * Returns the currently selected field value or empty string if no value is set.
24086      * @return {String} value The selected value
24087      */
24088     getValue : function(){
24089         if(this.valueField){
24090             return typeof this.value != 'undefined' ? this.value : '';
24091         }else{
24092             return Roo.form.ComboBox.superclass.getValue.call(this);
24093         }
24094     },
24095
24096     /**
24097      * Clears any text/value currently set in the field
24098      */
24099     clearValue : function(){
24100         if(this.hiddenField){
24101             this.hiddenField.value = '';
24102         }
24103         this.value = '';
24104         this.setRawValue('');
24105         this.lastSelectionText = '';
24106         
24107     },
24108
24109     /**
24110      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24111      * will be displayed in the field.  If the value does not match the data value of an existing item,
24112      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24113      * Otherwise the field will be blank (although the value will still be set).
24114      * @param {String} value The value to match
24115      */
24116     setValue : function(v){
24117         var text = v;
24118         if(this.valueField){
24119             var r = this.findRecord(this.valueField, v);
24120             if(r){
24121                 text = r.data[this.displayField];
24122             }else if(this.valueNotFoundText !== undefined){
24123                 text = this.valueNotFoundText;
24124             }
24125         }
24126         this.lastSelectionText = text;
24127         if(this.hiddenField){
24128             this.hiddenField.value = v;
24129         }
24130         Roo.form.ComboBox.superclass.setValue.call(this, text);
24131         this.value = v;
24132     },
24133     /**
24134      * @property {Object} the last set data for the element
24135      */
24136     
24137     lastData : false,
24138     /**
24139      * Sets the value of the field based on a object which is related to the record format for the store.
24140      * @param {Object} value the value to set as. or false on reset?
24141      */
24142     setFromData : function(o){
24143         var dv = ''; // display value
24144         var vv = ''; // value value..
24145         this.lastData = o;
24146         if (this.displayField) {
24147             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24148         } else {
24149             // this is an error condition!!!
24150             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24151         }
24152         
24153         if(this.valueField){
24154             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24155         }
24156         if(this.hiddenField){
24157             this.hiddenField.value = vv;
24158             
24159             this.lastSelectionText = dv;
24160             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24161             this.value = vv;
24162             return;
24163         }
24164         // no hidden field.. - we store the value in 'value', but still display
24165         // display field!!!!
24166         this.lastSelectionText = dv;
24167         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24168         this.value = vv;
24169         
24170         
24171     },
24172     // private
24173     reset : function(){
24174         // overridden so that last data is reset..
24175         this.setValue(this.originalValue);
24176         this.clearInvalid();
24177         this.lastData = false;
24178         if (this.view) {
24179             this.view.clearSelections();
24180         }
24181     },
24182     // private
24183     findRecord : function(prop, value){
24184         var record;
24185         if(this.store.getCount() > 0){
24186             this.store.each(function(r){
24187                 if(r.data[prop] == value){
24188                     record = r;
24189                     return false;
24190                 }
24191                 return true;
24192             });
24193         }
24194         return record;
24195     },
24196     
24197     getName: function()
24198     {
24199         // returns hidden if it's set..
24200         if (!this.rendered) {return ''};
24201         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24202         
24203     },
24204     // private
24205     onViewMove : function(e, t){
24206         this.inKeyMode = false;
24207     },
24208
24209     // private
24210     onViewOver : function(e, t){
24211         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24212             return;
24213         }
24214         var item = this.view.findItemFromChild(t);
24215         if(item){
24216             var index = this.view.indexOf(item);
24217             this.select(index, false);
24218         }
24219     },
24220
24221     // private
24222     onViewClick : function(doFocus)
24223     {
24224         var index = this.view.getSelectedIndexes()[0];
24225         var r = this.store.getAt(index);
24226         if(r){
24227             this.onSelect(r, index);
24228         }
24229         if(doFocus !== false && !this.blockFocus){
24230             this.el.focus();
24231         }
24232     },
24233
24234     // private
24235     restrictHeight : function(){
24236         this.innerList.dom.style.height = '';
24237         var inner = this.innerList.dom;
24238         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24239         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24240         this.list.beginUpdate();
24241         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24242         this.list.alignTo(this.el, this.listAlign);
24243         this.list.endUpdate();
24244     },
24245
24246     // private
24247     onEmptyResults : function(){
24248         this.collapse();
24249     },
24250
24251     /**
24252      * Returns true if the dropdown list is expanded, else false.
24253      */
24254     isExpanded : function(){
24255         return this.list.isVisible();
24256     },
24257
24258     /**
24259      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24260      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24261      * @param {String} value The data value of the item to select
24262      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24263      * selected item if it is not currently in view (defaults to true)
24264      * @return {Boolean} True if the value matched an item in the list, else false
24265      */
24266     selectByValue : function(v, scrollIntoView){
24267         if(v !== undefined && v !== null){
24268             var r = this.findRecord(this.valueField || this.displayField, v);
24269             if(r){
24270                 this.select(this.store.indexOf(r), scrollIntoView);
24271                 return true;
24272             }
24273         }
24274         return false;
24275     },
24276
24277     /**
24278      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24279      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24280      * @param {Number} index The zero-based index of the list item to select
24281      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24282      * selected item if it is not currently in view (defaults to true)
24283      */
24284     select : function(index, scrollIntoView){
24285         this.selectedIndex = index;
24286         this.view.select(index);
24287         if(scrollIntoView !== false){
24288             var el = this.view.getNode(index);
24289             if(el){
24290                 this.innerList.scrollChildIntoView(el, false);
24291             }
24292         }
24293     },
24294
24295     // private
24296     selectNext : function(){
24297         var ct = this.store.getCount();
24298         if(ct > 0){
24299             if(this.selectedIndex == -1){
24300                 this.select(0);
24301             }else if(this.selectedIndex < ct-1){
24302                 this.select(this.selectedIndex+1);
24303             }
24304         }
24305     },
24306
24307     // private
24308     selectPrev : function(){
24309         var ct = this.store.getCount();
24310         if(ct > 0){
24311             if(this.selectedIndex == -1){
24312                 this.select(0);
24313             }else if(this.selectedIndex != 0){
24314                 this.select(this.selectedIndex-1);
24315             }
24316         }
24317     },
24318
24319     // private
24320     onKeyUp : function(e){
24321         if(this.editable !== false && !e.isSpecialKey()){
24322             this.lastKey = e.getKey();
24323             this.dqTask.delay(this.queryDelay);
24324         }
24325     },
24326
24327     // private
24328     validateBlur : function(){
24329         return !this.list || !this.list.isVisible();   
24330     },
24331
24332     // private
24333     initQuery : function(){
24334         this.doQuery(this.getRawValue());
24335     },
24336
24337     // private
24338     doForce : function(){
24339         if(this.el.dom.value.length > 0){
24340             this.el.dom.value =
24341                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24342              
24343         }
24344     },
24345
24346     /**
24347      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24348      * query allowing the query action to be canceled if needed.
24349      * @param {String} query The SQL query to execute
24350      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24351      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24352      * saved in the current store (defaults to false)
24353      */
24354     doQuery : function(q, forceAll){
24355         if(q === undefined || q === null){
24356             q = '';
24357         }
24358         var qe = {
24359             query: q,
24360             forceAll: forceAll,
24361             combo: this,
24362             cancel:false
24363         };
24364         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24365             return false;
24366         }
24367         q = qe.query;
24368         forceAll = qe.forceAll;
24369         if(forceAll === true || (q.length >= this.minChars)){
24370             if(this.lastQuery != q || this.alwaysQuery){
24371                 this.lastQuery = q;
24372                 if(this.mode == 'local'){
24373                     this.selectedIndex = -1;
24374                     if(forceAll){
24375                         this.store.clearFilter();
24376                     }else{
24377                         this.store.filter(this.displayField, q);
24378                     }
24379                     this.onLoad();
24380                 }else{
24381                     this.store.baseParams[this.queryParam] = q;
24382                     this.store.load({
24383                         params: this.getParams(q)
24384                     });
24385                     this.expand();
24386                 }
24387             }else{
24388                 this.selectedIndex = -1;
24389                 this.onLoad();   
24390             }
24391         }
24392     },
24393
24394     // private
24395     getParams : function(q){
24396         var p = {};
24397         //p[this.queryParam] = q;
24398         if(this.pageSize){
24399             p.start = 0;
24400             p.limit = this.pageSize;
24401         }
24402         return p;
24403     },
24404
24405     /**
24406      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24407      */
24408     collapse : function(){
24409         if(!this.isExpanded()){
24410             return;
24411         }
24412         this.list.hide();
24413         Roo.get(document).un('mousedown', this.collapseIf, this);
24414         Roo.get(document).un('mousewheel', this.collapseIf, this);
24415         if (!this.editable) {
24416             Roo.get(document).un('keydown', this.listKeyPress, this);
24417         }
24418         this.fireEvent('collapse', this);
24419     },
24420
24421     // private
24422     collapseIf : function(e){
24423         if(!e.within(this.wrap) && !e.within(this.list)){
24424             this.collapse();
24425         }
24426     },
24427
24428     /**
24429      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24430      */
24431     expand : function(){
24432         if(this.isExpanded() || !this.hasFocus){
24433             return;
24434         }
24435         this.list.alignTo(this.el, this.listAlign);
24436         this.list.show();
24437         Roo.get(document).on('mousedown', this.collapseIf, this);
24438         Roo.get(document).on('mousewheel', this.collapseIf, this);
24439         if (!this.editable) {
24440             Roo.get(document).on('keydown', this.listKeyPress, this);
24441         }
24442         
24443         this.fireEvent('expand', this);
24444     },
24445
24446     // private
24447     // Implements the default empty TriggerField.onTriggerClick function
24448     onTriggerClick : function(){
24449         if(this.disabled){
24450             return;
24451         }
24452         if(this.isExpanded()){
24453             this.collapse();
24454             if (!this.blockFocus) {
24455                 this.el.focus();
24456             }
24457             
24458         }else {
24459             this.hasFocus = true;
24460             if(this.triggerAction == 'all') {
24461                 this.doQuery(this.allQuery, true);
24462             } else {
24463                 this.doQuery(this.getRawValue());
24464             }
24465             if (!this.blockFocus) {
24466                 this.el.focus();
24467             }
24468         }
24469     },
24470     listKeyPress : function(e)
24471     {
24472         //Roo.log('listkeypress');
24473         // scroll to first matching element based on key pres..
24474         if (e.isSpecialKey()) {
24475             return false;
24476         }
24477         var k = String.fromCharCode(e.getKey()).toUpperCase();
24478         //Roo.log(k);
24479         var match  = false;
24480         var csel = this.view.getSelectedNodes();
24481         var cselitem = false;
24482         if (csel.length) {
24483             var ix = this.view.indexOf(csel[0]);
24484             cselitem  = this.store.getAt(ix);
24485             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24486                 cselitem = false;
24487             }
24488             
24489         }
24490         
24491         this.store.each(function(v) { 
24492             if (cselitem) {
24493                 // start at existing selection.
24494                 if (cselitem.id == v.id) {
24495                     cselitem = false;
24496                 }
24497                 return;
24498             }
24499                 
24500             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24501                 match = this.store.indexOf(v);
24502                 return false;
24503             }
24504         }, this);
24505         
24506         if (match === false) {
24507             return true; // no more action?
24508         }
24509         // scroll to?
24510         this.view.select(match);
24511         var sn = Roo.get(this.view.getSelectedNodes()[0])
24512         sn.scrollIntoView(sn.dom.parentNode, false);
24513     }
24514
24515     /** 
24516     * @cfg {Boolean} grow 
24517     * @hide 
24518     */
24519     /** 
24520     * @cfg {Number} growMin 
24521     * @hide 
24522     */
24523     /** 
24524     * @cfg {Number} growMax 
24525     * @hide 
24526     */
24527     /**
24528      * @hide
24529      * @method autoSize
24530      */
24531 });/*
24532  * Copyright(c) 2010-2012, Roo J Solutions Limited
24533  *
24534  * Licence LGPL
24535  *
24536  */
24537
24538 /**
24539  * @class Roo.form.ComboBoxArray
24540  * @extends Roo.form.TextField
24541  * A facebook style adder... for lists of email / people / countries  etc...
24542  * pick multiple items from a combo box, and shows each one.
24543  *
24544  *  Fred [x]  Brian [x]  [Pick another |v]
24545  *
24546  *
24547  *  For this to work: it needs various extra information
24548  *    - normal combo problay has
24549  *      name, hiddenName
24550  *    + displayField, valueField
24551  *
24552  *    For our purpose...
24553  *
24554  *
24555  *   If we change from 'extends' to wrapping...
24556  *   
24557  *  
24558  *
24559  
24560  
24561  * @constructor
24562  * Create a new ComboBoxArray.
24563  * @param {Object} config Configuration options
24564  */
24565  
24566
24567 Roo.form.ComboBoxArray = function(config)
24568 {
24569     
24570     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24571     
24572     this.items = new Roo.util.MixedCollection(false);
24573     
24574     // construct the child combo...
24575     
24576     
24577     
24578     
24579    
24580     
24581 }
24582
24583  
24584 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24585
24586     /**
24587      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24588      */
24589     
24590     lastData : false,
24591     
24592     // behavies liek a hiddne field
24593     inputType:      'hidden',
24594     /**
24595      * @cfg {Number} width The width of the box that displays the selected element
24596      */ 
24597     width:          300,
24598
24599     
24600     
24601     /**
24602      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24603      */
24604     name : false,
24605     /**
24606      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24607      */
24608     hiddenName : false,
24609     
24610     
24611     // private the array of items that are displayed..
24612     items  : false,
24613     // private - the hidden field el.
24614     hiddenEl : false,
24615     // private - the filed el..
24616     el : false,
24617     
24618     //validateValue : function() { return true; }, // all values are ok!
24619     //onAddClick: function() { },
24620     
24621     onRender : function(ct, position) 
24622     {
24623         
24624         // create the standard hidden element
24625         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24626         
24627         
24628         // give fake names to child combo;
24629         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24630         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24631         
24632         this.combo = Roo.factory(this.combo, Roo.form);
24633         this.combo.onRender(ct, position);
24634         if (typeof(this.combo.width) != 'undefined') {
24635             this.combo.onResize(this.combo.width,0);
24636         }
24637         
24638         this.combo.initEvents();
24639         
24640         // assigned so form know we need to do this..
24641         this.store          = this.combo.store;
24642         this.valueField     = this.combo.valueField;
24643         this.displayField   = this.combo.displayField ;
24644         
24645         
24646         this.combo.wrap.addClass('x-cbarray-grp');
24647         
24648         var cbwrap = this.combo.wrap.createChild(
24649             {tag: 'div', cls: 'x-cbarray-cb'},
24650             this.combo.el.dom
24651         );
24652         
24653              
24654         this.hiddenEl = this.combo.wrap.createChild({
24655             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24656         });
24657         this.el = this.combo.wrap.createChild({
24658             tag: 'input',  type:'hidden' , name: this.name, value : ''
24659         });
24660          //   this.el.dom.removeAttribute("name");
24661         
24662         
24663         this.outerWrap = this.combo.wrap;
24664         this.wrap = cbwrap;
24665         
24666         this.outerWrap.setWidth(this.width);
24667         this.outerWrap.dom.removeChild(this.el.dom);
24668         
24669         this.wrap.dom.appendChild(this.el.dom);
24670         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24671         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24672         
24673         this.combo.trigger.setStyle('position','relative');
24674         this.combo.trigger.setStyle('left', '0px');
24675         this.combo.trigger.setStyle('top', '2px');
24676         
24677         this.combo.el.setStyle('vertical-align', 'text-bottom');
24678         
24679         //this.trigger.setStyle('vertical-align', 'top');
24680         
24681         // this should use the code from combo really... on('add' ....)
24682         if (this.adder) {
24683             
24684         
24685             this.adder = this.outerWrap.createChild(
24686                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24687             var _t = this;
24688             this.adder.on('click', function(e) {
24689                 _t.fireEvent('adderclick', this, e);
24690             }, _t);
24691         }
24692         //var _t = this;
24693         //this.adder.on('click', this.onAddClick, _t);
24694         
24695         
24696         this.combo.on('select', function(cb, rec, ix) {
24697             this.addItem(rec.data);
24698             
24699             cb.setValue('');
24700             cb.el.dom.value = '';
24701             //cb.lastData = rec.data;
24702             // add to list
24703             
24704         }, this);
24705         
24706         
24707     },
24708     
24709     
24710     getName: function()
24711     {
24712         // returns hidden if it's set..
24713         if (!this.rendered) {return ''};
24714         return  this.hiddenName ? this.hiddenName : this.name;
24715         
24716     },
24717     
24718     
24719     onResize: function(w, h){
24720         
24721         return;
24722         // not sure if this is needed..
24723         //this.combo.onResize(w,h);
24724         
24725         if(typeof w != 'number'){
24726             // we do not handle it!?!?
24727             return;
24728         }
24729         var tw = this.combo.trigger.getWidth();
24730         tw += this.addicon ? this.addicon.getWidth() : 0;
24731         tw += this.editicon ? this.editicon.getWidth() : 0;
24732         var x = w - tw;
24733         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24734             
24735         this.combo.trigger.setStyle('left', '0px');
24736         
24737         if(this.list && this.listWidth === undefined){
24738             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24739             this.list.setWidth(lw);
24740             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24741         }
24742         
24743     
24744         
24745     },
24746     
24747     addItem: function(rec)
24748     {
24749         var valueField = this.combo.valueField;
24750         var displayField = this.combo.displayField;
24751         if (this.items.indexOfKey(rec[valueField]) > -1) {
24752             //console.log("GOT " + rec.data.id);
24753             return;
24754         }
24755         
24756         var x = new Roo.form.ComboBoxArray.Item({
24757             //id : rec[this.idField],
24758             data : rec,
24759             displayField : displayField ,
24760             tipField : displayField ,
24761             cb : this
24762         });
24763         // use the 
24764         this.items.add(rec[valueField],x);
24765         // add it before the element..
24766         this.updateHiddenEl();
24767         x.render(this.outerWrap, this.wrap.dom);
24768         // add the image handler..
24769     },
24770     
24771     updateHiddenEl : function()
24772     {
24773         this.validate();
24774         if (!this.hiddenEl) {
24775             return;
24776         }
24777         var ar = [];
24778         var idField = this.combo.valueField;
24779         
24780         this.items.each(function(f) {
24781             ar.push(f.data[idField]);
24782            
24783         });
24784         this.hiddenEl.dom.value = ar.join(',');
24785         this.validate();
24786     },
24787     
24788     reset : function()
24789     {
24790         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24791         this.items.each(function(f) {
24792            f.remove(); 
24793         });
24794         this.el.dom.value = '';
24795         if (this.hiddenEl) {
24796             this.hiddenEl.dom.value = '';
24797         }
24798         
24799     },
24800     getValue: function()
24801     {
24802         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24803     },
24804     setValue: function(v) // not a valid action - must use addItems..
24805     {
24806          
24807         this.reset();
24808         
24809         
24810         
24811         if (this.store.isLocal && (typeof(v) == 'string')) {
24812             // then we can use the store to find the values..
24813             // comma seperated at present.. this needs to allow JSON based encoding..
24814             this.hiddenEl.value  = v;
24815             var v_ar = [];
24816             Roo.each(v.split(','), function(k) {
24817                 Roo.log("CHECK " + this.valueField + ',' + k);
24818                 var li = this.store.query(this.valueField, k);
24819                 if (!li.length) {
24820                     return;
24821                 }
24822                 var add = {};
24823                 add[this.valueField] = k;
24824                 add[this.displayField] = li.item(0).data[this.displayField];
24825                 
24826                 this.addItem(add);
24827             }, this) 
24828              
24829         }
24830         if (typeof(v) == 'object') {
24831             // then let's assume it's an array of objects..
24832             Roo.each(v, function(l) {
24833                 this.addItem(l);
24834             }, this);
24835              
24836         }
24837         
24838         
24839     },
24840     setFromData: function(v)
24841     {
24842         // this recieves an object, if setValues is called.
24843         this.reset();
24844         this.el.dom.value = v[this.displayField];
24845         this.hiddenEl.dom.value = v[this.valueField];
24846         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24847             return;
24848         }
24849         var kv = v[this.valueField];
24850         var dv = v[this.displayField];
24851         kv = typeof(kv) != 'string' ? '' : kv;
24852         dv = typeof(dv) != 'string' ? '' : dv;
24853         
24854         
24855         var keys = kv.split(',');
24856         var display = dv.split(',');
24857         for (var i = 0 ; i < keys.length; i++) {
24858             
24859             add = {};
24860             add[this.valueField] = keys[i];
24861             add[this.displayField] = display[i];
24862             this.addItem(add);
24863         }
24864       
24865         
24866     },
24867     
24868     
24869     validateValue : function(value){
24870         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24871         
24872     }
24873     
24874 });
24875
24876
24877
24878 /**
24879  * @class Roo.form.ComboBoxArray.Item
24880  * @extends Roo.BoxComponent
24881  * A selected item in the list
24882  *  Fred [x]  Brian [x]  [Pick another |v]
24883  * 
24884  * @constructor
24885  * Create a new item.
24886  * @param {Object} config Configuration options
24887  */
24888  
24889 Roo.form.ComboBoxArray.Item = function(config) {
24890     config.id = Roo.id();
24891     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24892 }
24893
24894 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24895     data : {},
24896     cb: false,
24897     displayField : false,
24898     tipField : false,
24899     
24900     
24901     defaultAutoCreate : {
24902         tag: 'div',
24903         cls: 'x-cbarray-item',
24904         cn : [ 
24905             { tag: 'div' },
24906             {
24907                 tag: 'img',
24908                 width:16,
24909                 height : 16,
24910                 src : Roo.BLANK_IMAGE_URL ,
24911                 align: 'center'
24912             }
24913         ]
24914         
24915     },
24916     
24917  
24918     onRender : function(ct, position)
24919     {
24920         Roo.form.Field.superclass.onRender.call(this, ct, position);
24921         
24922         if(!this.el){
24923             var cfg = this.getAutoCreate();
24924             this.el = ct.createChild(cfg, position);
24925         }
24926         
24927         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24928         
24929         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24930             this.cb.renderer(this.data) :
24931             String.format('{0}',this.data[this.displayField]);
24932         
24933             
24934         this.el.child('div').dom.setAttribute('qtip',
24935                         String.format('{0}',this.data[this.tipField])
24936         );
24937         
24938         this.el.child('img').on('click', this.remove, this);
24939         
24940     },
24941    
24942     remove : function()
24943     {
24944         
24945         this.cb.items.remove(this);
24946         this.el.child('img').un('click', this.remove, this);
24947         this.el.remove();
24948         this.cb.updateHiddenEl();
24949     }
24950     
24951     
24952 });/*
24953  * Based on:
24954  * Ext JS Library 1.1.1
24955  * Copyright(c) 2006-2007, Ext JS, LLC.
24956  *
24957  * Originally Released Under LGPL - original licence link has changed is not relivant.
24958  *
24959  * Fork - LGPL
24960  * <script type="text/javascript">
24961  */
24962 /**
24963  * @class Roo.form.Checkbox
24964  * @extends Roo.form.Field
24965  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24966  * @constructor
24967  * Creates a new Checkbox
24968  * @param {Object} config Configuration options
24969  */
24970 Roo.form.Checkbox = function(config){
24971     Roo.form.Checkbox.superclass.constructor.call(this, config);
24972     this.addEvents({
24973         /**
24974          * @event check
24975          * Fires when the checkbox is checked or unchecked.
24976              * @param {Roo.form.Checkbox} this This checkbox
24977              * @param {Boolean} checked The new checked value
24978              */
24979         check : true
24980     });
24981 };
24982
24983 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24984     /**
24985      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24986      */
24987     focusClass : undefined,
24988     /**
24989      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24990      */
24991     fieldClass: "x-form-field",
24992     /**
24993      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24994      */
24995     checked: false,
24996     /**
24997      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24998      * {tag: "input", type: "checkbox", autocomplete: "off"})
24999      */
25000     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
25001     /**
25002      * @cfg {String} boxLabel The text that appears beside the checkbox
25003      */
25004     boxLabel : "",
25005     /**
25006      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25007      */  
25008     inputValue : '1',
25009     /**
25010      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25011      */
25012      valueOff: '0', // value when not checked..
25013
25014     actionMode : 'viewEl', 
25015     //
25016     // private
25017     itemCls : 'x-menu-check-item x-form-item',
25018     groupClass : 'x-menu-group-item',
25019     inputType : 'hidden',
25020     
25021     
25022     inSetChecked: false, // check that we are not calling self...
25023     
25024     inputElement: false, // real input element?
25025     basedOn: false, // ????
25026     
25027     isFormField: true, // not sure where this is needed!!!!
25028
25029     onResize : function(){
25030         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25031         if(!this.boxLabel){
25032             this.el.alignTo(this.wrap, 'c-c');
25033         }
25034     },
25035
25036     initEvents : function(){
25037         Roo.form.Checkbox.superclass.initEvents.call(this);
25038         this.el.on("click", this.onClick,  this);
25039         this.el.on("change", this.onClick,  this);
25040     },
25041
25042
25043     getResizeEl : function(){
25044         return this.wrap;
25045     },
25046
25047     getPositionEl : function(){
25048         return this.wrap;
25049     },
25050
25051     // private
25052     onRender : function(ct, position){
25053         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25054         /*
25055         if(this.inputValue !== undefined){
25056             this.el.dom.value = this.inputValue;
25057         }
25058         */
25059         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25060         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25061         var viewEl = this.wrap.createChild({ 
25062             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25063         this.viewEl = viewEl;   
25064         this.wrap.on('click', this.onClick,  this); 
25065         
25066         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25067         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25068         
25069         
25070         
25071         if(this.boxLabel){
25072             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25073         //    viewEl.on('click', this.onClick,  this); 
25074         }
25075         //if(this.checked){
25076             this.setChecked(this.checked);
25077         //}else{
25078             //this.checked = this.el.dom;
25079         //}
25080
25081     },
25082
25083     // private
25084     initValue : Roo.emptyFn,
25085
25086     /**
25087      * Returns the checked state of the checkbox.
25088      * @return {Boolean} True if checked, else false
25089      */
25090     getValue : function(){
25091         if(this.el){
25092             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25093         }
25094         return this.valueOff;
25095         
25096     },
25097
25098         // private
25099     onClick : function(){ 
25100         this.setChecked(!this.checked);
25101
25102         //if(this.el.dom.checked != this.checked){
25103         //    this.setValue(this.el.dom.checked);
25104        // }
25105     },
25106
25107     /**
25108      * Sets the checked state of the checkbox.
25109      * On is always based on a string comparison between inputValue and the param.
25110      * @param {Boolean/String} value - the value to set 
25111      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25112      */
25113     setValue : function(v,suppressEvent){
25114         
25115         
25116         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25117         //if(this.el && this.el.dom){
25118         //    this.el.dom.checked = this.checked;
25119         //    this.el.dom.defaultChecked = this.checked;
25120         //}
25121         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25122         //this.fireEvent("check", this, this.checked);
25123     },
25124     // private..
25125     setChecked : function(state,suppressEvent)
25126     {
25127         if (this.inSetChecked) {
25128             this.checked = state;
25129             return;
25130         }
25131         
25132     
25133         if(this.wrap){
25134             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25135         }
25136         this.checked = state;
25137         if(suppressEvent !== true){
25138             this.fireEvent('check', this, state);
25139         }
25140         this.inSetChecked = true;
25141         this.el.dom.value = state ? this.inputValue : this.valueOff;
25142         this.inSetChecked = false;
25143         
25144     },
25145     // handle setting of hidden value by some other method!!?!?
25146     setFromHidden: function()
25147     {
25148         if(!this.el){
25149             return;
25150         }
25151         //console.log("SET FROM HIDDEN");
25152         //alert('setFrom hidden');
25153         this.setValue(this.el.dom.value);
25154     },
25155     
25156     onDestroy : function()
25157     {
25158         if(this.viewEl){
25159             Roo.get(this.viewEl).remove();
25160         }
25161          
25162         Roo.form.Checkbox.superclass.onDestroy.call(this);
25163     }
25164
25165 });/*
25166  * Based on:
25167  * Ext JS Library 1.1.1
25168  * Copyright(c) 2006-2007, Ext JS, LLC.
25169  *
25170  * Originally Released Under LGPL - original licence link has changed is not relivant.
25171  *
25172  * Fork - LGPL
25173  * <script type="text/javascript">
25174  */
25175  
25176 /**
25177  * @class Roo.form.Radio
25178  * @extends Roo.form.Checkbox
25179  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25180  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25181  * @constructor
25182  * Creates a new Radio
25183  * @param {Object} config Configuration options
25184  */
25185 Roo.form.Radio = function(){
25186     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25187 };
25188 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25189     inputType: 'radio',
25190
25191     /**
25192      * If this radio is part of a group, it will return the selected value
25193      * @return {String}
25194      */
25195     getGroupValue : function(){
25196         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25197     },
25198     
25199     
25200     onRender : function(ct, position){
25201         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25202         
25203         if(this.inputValue !== undefined){
25204             this.el.dom.value = this.inputValue;
25205         }
25206          
25207         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25208         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25209         //var viewEl = this.wrap.createChild({ 
25210         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25211         //this.viewEl = viewEl;   
25212         //this.wrap.on('click', this.onClick,  this); 
25213         
25214         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25215         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25216         
25217         
25218         
25219         if(this.boxLabel){
25220             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25221         //    viewEl.on('click', this.onClick,  this); 
25222         }
25223          if(this.checked){
25224             this.el.dom.checked =   'checked' ;
25225         }
25226          
25227     } 
25228     
25229     
25230 });//<script type="text/javascript">
25231
25232 /*
25233  * Ext JS Library 1.1.1
25234  * Copyright(c) 2006-2007, Ext JS, LLC.
25235  * licensing@extjs.com
25236  * 
25237  * http://www.extjs.com/license
25238  */
25239  
25240  /*
25241   * 
25242   * Known bugs:
25243   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25244   * - IE ? - no idea how much works there.
25245   * 
25246   * 
25247   * 
25248   */
25249  
25250
25251 /**
25252  * @class Ext.form.HtmlEditor
25253  * @extends Ext.form.Field
25254  * Provides a lightweight HTML Editor component.
25255  *
25256  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25257  * 
25258  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25259  * supported by this editor.</b><br/><br/>
25260  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25261  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25262  */
25263 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25264       /**
25265      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25266      */
25267     toolbars : false,
25268     /**
25269      * @cfg {String} createLinkText The default text for the create link prompt
25270      */
25271     createLinkText : 'Please enter the URL for the link:',
25272     /**
25273      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25274      */
25275     defaultLinkValue : 'http:/'+'/',
25276    
25277      /**
25278      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25279      *                        Roo.resizable.
25280      */
25281     resizable : false,
25282      /**
25283      * @cfg {Number} height (in pixels)
25284      */   
25285     height: 300,
25286    /**
25287      * @cfg {Number} width (in pixels)
25288      */   
25289     width: 500,
25290     
25291     /**
25292      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25293      * 
25294      */
25295     stylesheets: false,
25296     
25297     // id of frame..
25298     frameId: false,
25299     
25300     // private properties
25301     validationEvent : false,
25302     deferHeight: true,
25303     initialized : false,
25304     activated : false,
25305     sourceEditMode : false,
25306     onFocus : Roo.emptyFn,
25307     iframePad:3,
25308     hideMode:'offsets',
25309     
25310     defaultAutoCreate : { // modified by initCompnoent..
25311         tag: "textarea",
25312         style:"width:500px;height:300px;",
25313         autocomplete: "off"
25314     },
25315
25316     // private
25317     initComponent : function(){
25318         this.addEvents({
25319             /**
25320              * @event initialize
25321              * Fires when the editor is fully initialized (including the iframe)
25322              * @param {HtmlEditor} this
25323              */
25324             initialize: true,
25325             /**
25326              * @event activate
25327              * Fires when the editor is first receives the focus. Any insertion must wait
25328              * until after this event.
25329              * @param {HtmlEditor} this
25330              */
25331             activate: true,
25332              /**
25333              * @event beforesync
25334              * Fires before the textarea is updated with content from the editor iframe. Return false
25335              * to cancel the sync.
25336              * @param {HtmlEditor} this
25337              * @param {String} html
25338              */
25339             beforesync: true,
25340              /**
25341              * @event beforepush
25342              * Fires before the iframe editor is updated with content from the textarea. Return false
25343              * to cancel the push.
25344              * @param {HtmlEditor} this
25345              * @param {String} html
25346              */
25347             beforepush: true,
25348              /**
25349              * @event sync
25350              * Fires when the textarea is updated with content from the editor iframe.
25351              * @param {HtmlEditor} this
25352              * @param {String} html
25353              */
25354             sync: true,
25355              /**
25356              * @event push
25357              * Fires when the iframe editor is updated with content from the textarea.
25358              * @param {HtmlEditor} this
25359              * @param {String} html
25360              */
25361             push: true,
25362              /**
25363              * @event editmodechange
25364              * Fires when the editor switches edit modes
25365              * @param {HtmlEditor} this
25366              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25367              */
25368             editmodechange: true,
25369             /**
25370              * @event editorevent
25371              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25372              * @param {HtmlEditor} this
25373              */
25374             editorevent: true
25375         });
25376         this.defaultAutoCreate =  {
25377             tag: "textarea",
25378             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25379             autocomplete: "off"
25380         };
25381     },
25382
25383     /**
25384      * Protected method that will not generally be called directly. It
25385      * is called when the editor creates its toolbar. Override this method if you need to
25386      * add custom toolbar buttons.
25387      * @param {HtmlEditor} editor
25388      */
25389     createToolbar : function(editor){
25390         if (!editor.toolbars || !editor.toolbars.length) {
25391             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25392         }
25393         
25394         for (var i =0 ; i < editor.toolbars.length;i++) {
25395             editor.toolbars[i] = Roo.factory(
25396                     typeof(editor.toolbars[i]) == 'string' ?
25397                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25398                 Roo.form.HtmlEditor);
25399             editor.toolbars[i].init(editor);
25400         }
25401          
25402         
25403     },
25404
25405     /**
25406      * Protected method that will not generally be called directly. It
25407      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25408      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25409      */
25410     getDocMarkup : function(){
25411         // body styles..
25412         var st = '';
25413         if (this.stylesheets === false) {
25414             
25415             Roo.get(document.head).select('style').each(function(node) {
25416                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25417             });
25418             
25419             Roo.get(document.head).select('link').each(function(node) { 
25420                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25421             });
25422             
25423         } else if (!this.stylesheets.length) {
25424                 // simple..
25425                 st = '<style type="text/css">' +
25426                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25427                    '</style>';
25428         } else {
25429             Roo.each(this.stylesheets, function(s) {
25430                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25431             });
25432             
25433         }
25434         
25435         st +=  '<style type="text/css">' +
25436             'IMG { cursor: pointer } ' +
25437         '</style>';
25438
25439         
25440         return '<html><head>' + st  +
25441             //<style type="text/css">' +
25442             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25443             //'</style>' +
25444             ' </head><body class="roo-htmleditor-body"></body></html>';
25445     },
25446
25447     // private
25448     onRender : function(ct, position)
25449     {
25450         var _t = this;
25451         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25452         this.el.dom.style.border = '0 none';
25453         this.el.dom.setAttribute('tabIndex', -1);
25454         this.el.addClass('x-hidden');
25455         if(Roo.isIE){ // fix IE 1px bogus margin
25456             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25457         }
25458         this.wrap = this.el.wrap({
25459             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25460         });
25461         
25462         if (this.resizable) {
25463             this.resizeEl = new Roo.Resizable(this.wrap, {
25464                 pinned : true,
25465                 wrap: true,
25466                 dynamic : true,
25467                 minHeight : this.height,
25468                 height: this.height,
25469                 handles : this.resizable,
25470                 width: this.width,
25471                 listeners : {
25472                     resize : function(r, w, h) {
25473                         _t.onResize(w,h); // -something
25474                     }
25475                 }
25476             });
25477             
25478         }
25479
25480         this.frameId = Roo.id();
25481         
25482         this.createToolbar(this);
25483         
25484       
25485         
25486         var iframe = this.wrap.createChild({
25487             tag: 'iframe',
25488             id: this.frameId,
25489             name: this.frameId,
25490             frameBorder : 'no',
25491             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25492         }, this.el
25493         );
25494         
25495        // console.log(iframe);
25496         //this.wrap.dom.appendChild(iframe);
25497
25498         this.iframe = iframe.dom;
25499
25500          this.assignDocWin();
25501         
25502         this.doc.designMode = 'on';
25503        
25504         this.doc.open();
25505         this.doc.write(this.getDocMarkup());
25506         this.doc.close();
25507
25508         
25509         var task = { // must defer to wait for browser to be ready
25510             run : function(){
25511                 //console.log("run task?" + this.doc.readyState);
25512                 this.assignDocWin();
25513                 if(this.doc.body || this.doc.readyState == 'complete'){
25514                     try {
25515                         this.doc.designMode="on";
25516                     } catch (e) {
25517                         return;
25518                     }
25519                     Roo.TaskMgr.stop(task);
25520                     this.initEditor.defer(10, this);
25521                 }
25522             },
25523             interval : 10,
25524             duration:10000,
25525             scope: this
25526         };
25527         Roo.TaskMgr.start(task);
25528
25529         if(!this.width){
25530             this.setSize(this.wrap.getSize());
25531         }
25532         if (this.resizeEl) {
25533             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25534             // should trigger onReize..
25535         }
25536     },
25537
25538     // private
25539     onResize : function(w, h)
25540     {
25541         //Roo.log('resize: ' +w + ',' + h );
25542         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25543         if(this.el && this.iframe){
25544             if(typeof w == 'number'){
25545                 var aw = w - this.wrap.getFrameWidth('lr');
25546                 this.el.setWidth(this.adjustWidth('textarea', aw));
25547                 this.iframe.style.width = aw + 'px';
25548             }
25549             if(typeof h == 'number'){
25550                 var tbh = 0;
25551                 for (var i =0; i < this.toolbars.length;i++) {
25552                     // fixme - ask toolbars for heights?
25553                     tbh += this.toolbars[i].tb.el.getHeight();
25554                     if (this.toolbars[i].footer) {
25555                         tbh += this.toolbars[i].footer.el.getHeight();
25556                     }
25557                 }
25558                 
25559                 
25560                 
25561                 
25562                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25563                 ah -= 5; // knock a few pixes off for look..
25564                 this.el.setHeight(this.adjustWidth('textarea', ah));
25565                 this.iframe.style.height = ah + 'px';
25566                 if(this.doc){
25567                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25568                 }
25569             }
25570         }
25571     },
25572
25573     /**
25574      * Toggles the editor between standard and source edit mode.
25575      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25576      */
25577     toggleSourceEdit : function(sourceEditMode){
25578         
25579         this.sourceEditMode = sourceEditMode === true;
25580         
25581         if(this.sourceEditMode){
25582 //            Roo.log('in');
25583 //            Roo.log(this.syncValue());
25584             this.syncValue();
25585             this.iframe.className = 'x-hidden';
25586             this.el.removeClass('x-hidden');
25587             this.el.dom.removeAttribute('tabIndex');
25588             this.el.focus();
25589         }else{
25590 //            Roo.log('out')
25591 //            Roo.log(this.pushValue()); 
25592             this.pushValue();
25593             this.iframe.className = '';
25594             this.el.addClass('x-hidden');
25595             this.el.dom.setAttribute('tabIndex', -1);
25596             this.deferFocus();
25597         }
25598         this.setSize(this.wrap.getSize());
25599         this.fireEvent('editmodechange', this, this.sourceEditMode);
25600     },
25601
25602     // private used internally
25603     createLink : function(){
25604         var url = prompt(this.createLinkText, this.defaultLinkValue);
25605         if(url && url != 'http:/'+'/'){
25606             this.relayCmd('createlink', url);
25607         }
25608     },
25609
25610     // private (for BoxComponent)
25611     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25612
25613     // private (for BoxComponent)
25614     getResizeEl : function(){
25615         return this.wrap;
25616     },
25617
25618     // private (for BoxComponent)
25619     getPositionEl : function(){
25620         return this.wrap;
25621     },
25622
25623     // private
25624     initEvents : function(){
25625         this.originalValue = this.getValue();
25626     },
25627
25628     /**
25629      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25630      * @method
25631      */
25632     markInvalid : Roo.emptyFn,
25633     /**
25634      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25635      * @method
25636      */
25637     clearInvalid : Roo.emptyFn,
25638
25639     setValue : function(v){
25640         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25641         this.pushValue();
25642     },
25643
25644     /**
25645      * Protected method that will not generally be called directly. If you need/want
25646      * custom HTML cleanup, this is the method you should override.
25647      * @param {String} html The HTML to be cleaned
25648      * return {String} The cleaned HTML
25649      */
25650     cleanHtml : function(html){
25651         html = String(html);
25652         if(html.length > 5){
25653             if(Roo.isSafari){ // strip safari nonsense
25654                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25655             }
25656         }
25657         if(html == '&nbsp;'){
25658             html = '';
25659         }
25660         return html;
25661     },
25662
25663     /**
25664      * Protected method that will not generally be called directly. Syncs the contents
25665      * of the editor iframe with the textarea.
25666      */
25667     syncValue : function(){
25668         if(this.initialized){
25669             var bd = (this.doc.body || this.doc.documentElement);
25670             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25671             var html = bd.innerHTML;
25672             if(Roo.isSafari){
25673                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25674                 var m = bs.match(/text-align:(.*?);/i);
25675                 if(m && m[1]){
25676                     html = '<div style="'+m[0]+'">' + html + '</div>';
25677                 }
25678             }
25679             html = this.cleanHtml(html);
25680             // fix up the special chars.. normaly like back quotes in word...
25681             // however we do not want to do this with chinese..
25682             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25683                 var cc = b.charCodeAt();
25684                 if (
25685                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25686                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25687                     (cc >= 0xf900 && cc < 0xfb00 )
25688                 ) {
25689                         return b;
25690                 }
25691                 return "&#"+cc+";" 
25692             });
25693             if(this.fireEvent('beforesync', this, html) !== false){
25694                 this.el.dom.value = html;
25695                 this.fireEvent('sync', this, html);
25696             }
25697         }
25698     },
25699
25700     /**
25701      * Protected method that will not generally be called directly. Pushes the value of the textarea
25702      * into the iframe editor.
25703      */
25704     pushValue : function(){
25705         if(this.initialized){
25706             var v = this.el.dom.value;
25707             
25708             if(v.length < 1){
25709                 v = '&#160;';
25710             }
25711             
25712             if(this.fireEvent('beforepush', this, v) !== false){
25713                 var d = (this.doc.body || this.doc.documentElement);
25714                 d.innerHTML = v;
25715                 this.cleanUpPaste();
25716                 this.el.dom.value = d.innerHTML;
25717                 this.fireEvent('push', this, v);
25718             }
25719         }
25720     },
25721
25722     // private
25723     deferFocus : function(){
25724         this.focus.defer(10, this);
25725     },
25726
25727     // doc'ed in Field
25728     focus : function(){
25729         if(this.win && !this.sourceEditMode){
25730             this.win.focus();
25731         }else{
25732             this.el.focus();
25733         }
25734     },
25735     
25736     assignDocWin: function()
25737     {
25738         var iframe = this.iframe;
25739         
25740          if(Roo.isIE){
25741             this.doc = iframe.contentWindow.document;
25742             this.win = iframe.contentWindow;
25743         } else {
25744             if (!Roo.get(this.frameId)) {
25745                 return;
25746             }
25747             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25748             this.win = Roo.get(this.frameId).dom.contentWindow;
25749         }
25750     },
25751     
25752     // private
25753     initEditor : function(){
25754         //console.log("INIT EDITOR");
25755         this.assignDocWin();
25756         
25757         
25758         
25759         this.doc.designMode="on";
25760         this.doc.open();
25761         this.doc.write(this.getDocMarkup());
25762         this.doc.close();
25763         
25764         var dbody = (this.doc.body || this.doc.documentElement);
25765         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25766         // this copies styles from the containing element into thsi one..
25767         // not sure why we need all of this..
25768         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25769         ss['background-attachment'] = 'fixed'; // w3c
25770         dbody.bgProperties = 'fixed'; // ie
25771         Roo.DomHelper.applyStyles(dbody, ss);
25772         Roo.EventManager.on(this.doc, {
25773             //'mousedown': this.onEditorEvent,
25774             'mouseup': this.onEditorEvent,
25775             'dblclick': this.onEditorEvent,
25776             'click': this.onEditorEvent,
25777             'keyup': this.onEditorEvent,
25778             buffer:100,
25779             scope: this
25780         });
25781         if(Roo.isGecko){
25782             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25783         }
25784         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25785             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25786         }
25787         this.initialized = true;
25788
25789         this.fireEvent('initialize', this);
25790         this.pushValue();
25791     },
25792
25793     // private
25794     onDestroy : function(){
25795         
25796         
25797         
25798         if(this.rendered){
25799             
25800             for (var i =0; i < this.toolbars.length;i++) {
25801                 // fixme - ask toolbars for heights?
25802                 this.toolbars[i].onDestroy();
25803             }
25804             
25805             this.wrap.dom.innerHTML = '';
25806             this.wrap.remove();
25807         }
25808     },
25809
25810     // private
25811     onFirstFocus : function(){
25812         
25813         this.assignDocWin();
25814         
25815         
25816         this.activated = true;
25817         for (var i =0; i < this.toolbars.length;i++) {
25818             this.toolbars[i].onFirstFocus();
25819         }
25820        
25821         if(Roo.isGecko){ // prevent silly gecko errors
25822             this.win.focus();
25823             var s = this.win.getSelection();
25824             if(!s.focusNode || s.focusNode.nodeType != 3){
25825                 var r = s.getRangeAt(0);
25826                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25827                 r.collapse(true);
25828                 this.deferFocus();
25829             }
25830             try{
25831                 this.execCmd('useCSS', true);
25832                 this.execCmd('styleWithCSS', false);
25833             }catch(e){}
25834         }
25835         this.fireEvent('activate', this);
25836     },
25837
25838     // private
25839     adjustFont: function(btn){
25840         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25841         //if(Roo.isSafari){ // safari
25842         //    adjust *= 2;
25843        // }
25844         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25845         if(Roo.isSafari){ // safari
25846             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25847             v =  (v < 10) ? 10 : v;
25848             v =  (v > 48) ? 48 : v;
25849             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25850             
25851         }
25852         
25853         
25854         v = Math.max(1, v+adjust);
25855         
25856         this.execCmd('FontSize', v  );
25857     },
25858
25859     onEditorEvent : function(e){
25860         this.fireEvent('editorevent', this, e);
25861       //  this.updateToolbar();
25862         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25863     },
25864
25865     insertTag : function(tg)
25866     {
25867         // could be a bit smarter... -> wrap the current selected tRoo..
25868         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25869             
25870             range = this.createRange(this.getSelection());
25871             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25872             wrappingNode.appendChild(range.extractContents());
25873             range.insertNode(wrappingNode);
25874
25875             return;
25876             
25877             
25878             
25879         }
25880         this.execCmd("formatblock",   tg);
25881         
25882     },
25883     
25884     insertText : function(txt)
25885     {
25886         
25887         
25888         var range = this.createRange();
25889         range.deleteContents();
25890                //alert(Sender.getAttribute('label'));
25891                
25892         range.insertNode(this.doc.createTextNode(txt));
25893     } ,
25894     
25895     // private
25896     relayBtnCmd : function(btn){
25897         this.relayCmd(btn.cmd);
25898     },
25899
25900     /**
25901      * Executes a Midas editor command on the editor document and performs necessary focus and
25902      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25903      * @param {String} cmd The Midas command
25904      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25905      */
25906     relayCmd : function(cmd, value){
25907         this.win.focus();
25908         this.execCmd(cmd, value);
25909         this.fireEvent('editorevent', this);
25910         //this.updateToolbar();
25911         this.deferFocus();
25912     },
25913
25914     /**
25915      * Executes a Midas editor command directly on the editor document.
25916      * For visual commands, you should use {@link #relayCmd} instead.
25917      * <b>This should only be called after the editor is initialized.</b>
25918      * @param {String} cmd The Midas command
25919      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25920      */
25921     execCmd : function(cmd, value){
25922         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25923         this.syncValue();
25924     },
25925  
25926  
25927    
25928     /**
25929      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25930      * to insert tRoo.
25931      * @param {String} text | dom node.. 
25932      */
25933     insertAtCursor : function(text)
25934     {
25935         
25936         
25937         
25938         if(!this.activated){
25939             return;
25940         }
25941         /*
25942         if(Roo.isIE){
25943             this.win.focus();
25944             var r = this.doc.selection.createRange();
25945             if(r){
25946                 r.collapse(true);
25947                 r.pasteHTML(text);
25948                 this.syncValue();
25949                 this.deferFocus();
25950             
25951             }
25952             return;
25953         }
25954         */
25955         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25956             this.win.focus();
25957             
25958             
25959             // from jquery ui (MIT licenced)
25960             var range, node;
25961             var win = this.win;
25962             
25963             if (win.getSelection && win.getSelection().getRangeAt) {
25964                 range = win.getSelection().getRangeAt(0);
25965                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25966                 range.insertNode(node);
25967             } else if (win.document.selection && win.document.selection.createRange) {
25968                 // no firefox support
25969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25970                 win.document.selection.createRange().pasteHTML(txt);
25971             } else {
25972                 // no firefox support
25973                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25974                 this.execCmd('InsertHTML', txt);
25975             } 
25976             
25977             this.syncValue();
25978             
25979             this.deferFocus();
25980         }
25981     },
25982  // private
25983     mozKeyPress : function(e){
25984         if(e.ctrlKey){
25985             var c = e.getCharCode(), cmd;
25986           
25987             if(c > 0){
25988                 c = String.fromCharCode(c).toLowerCase();
25989                 switch(c){
25990                     case 'b':
25991                         cmd = 'bold';
25992                         break;
25993                     case 'i':
25994                         cmd = 'italic';
25995                         break;
25996                     
25997                     case 'u':
25998                         cmd = 'underline';
25999                         break;
26000                     
26001                     case 'v':
26002                         this.cleanUpPaste.defer(100, this);
26003                         return;
26004                         
26005                 }
26006                 if(cmd){
26007                     this.win.focus();
26008                     this.execCmd(cmd);
26009                     this.deferFocus();
26010                     e.preventDefault();
26011                 }
26012                 
26013             }
26014         }
26015     },
26016
26017     // private
26018     fixKeys : function(){ // load time branching for fastest keydown performance
26019         if(Roo.isIE){
26020             return function(e){
26021                 var k = e.getKey(), r;
26022                 if(k == e.TAB){
26023                     e.stopEvent();
26024                     r = this.doc.selection.createRange();
26025                     if(r){
26026                         r.collapse(true);
26027                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26028                         this.deferFocus();
26029                     }
26030                     return;
26031                 }
26032                 
26033                 if(k == e.ENTER){
26034                     r = this.doc.selection.createRange();
26035                     if(r){
26036                         var target = r.parentElement();
26037                         if(!target || target.tagName.toLowerCase() != 'li'){
26038                             e.stopEvent();
26039                             r.pasteHTML('<br />');
26040                             r.collapse(false);
26041                             r.select();
26042                         }
26043                     }
26044                 }
26045                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26046                     this.cleanUpPaste.defer(100, this);
26047                     return;
26048                 }
26049                 
26050                 
26051             };
26052         }else if(Roo.isOpera){
26053             return function(e){
26054                 var k = e.getKey();
26055                 if(k == e.TAB){
26056                     e.stopEvent();
26057                     this.win.focus();
26058                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26059                     this.deferFocus();
26060                 }
26061                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26062                     this.cleanUpPaste.defer(100, this);
26063                     return;
26064                 }
26065                 
26066             };
26067         }else if(Roo.isSafari){
26068             return function(e){
26069                 var k = e.getKey();
26070                 
26071                 if(k == e.TAB){
26072                     e.stopEvent();
26073                     this.execCmd('InsertText','\t');
26074                     this.deferFocus();
26075                     return;
26076                 }
26077                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26078                     this.cleanUpPaste.defer(100, this);
26079                     return;
26080                 }
26081                 
26082              };
26083         }
26084     }(),
26085     
26086     getAllAncestors: function()
26087     {
26088         var p = this.getSelectedNode();
26089         var a = [];
26090         if (!p) {
26091             a.push(p); // push blank onto stack..
26092             p = this.getParentElement();
26093         }
26094         
26095         
26096         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26097             a.push(p);
26098             p = p.parentNode;
26099         }
26100         a.push(this.doc.body);
26101         return a;
26102     },
26103     lastSel : false,
26104     lastSelNode : false,
26105     
26106     
26107     getSelection : function() 
26108     {
26109         this.assignDocWin();
26110         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26111     },
26112     
26113     getSelectedNode: function() 
26114     {
26115         // this may only work on Gecko!!!
26116         
26117         // should we cache this!!!!
26118         
26119         
26120         
26121          
26122         var range = this.createRange(this.getSelection()).cloneRange();
26123         
26124         if (Roo.isIE) {
26125             var parent = range.parentElement();
26126             while (true) {
26127                 var testRange = range.duplicate();
26128                 testRange.moveToElementText(parent);
26129                 if (testRange.inRange(range)) {
26130                     break;
26131                 }
26132                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26133                     break;
26134                 }
26135                 parent = parent.parentElement;
26136             }
26137             return parent;
26138         }
26139         
26140         // is ancestor a text element.
26141         var ac =  range.commonAncestorContainer;
26142         if (ac.nodeType == 3) {
26143             ac = ac.parentNode;
26144         }
26145         
26146         var ar = ac.childNodes;
26147          
26148         var nodes = [];
26149         var other_nodes = [];
26150         var has_other_nodes = false;
26151         for (var i=0;i<ar.length;i++) {
26152             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26153                 continue;
26154             }
26155             // fullly contained node.
26156             
26157             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26158                 nodes.push(ar[i]);
26159                 continue;
26160             }
26161             
26162             // probably selected..
26163             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26164                 other_nodes.push(ar[i]);
26165                 continue;
26166             }
26167             // outer..
26168             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26169                 continue;
26170             }
26171             
26172             
26173             has_other_nodes = true;
26174         }
26175         if (!nodes.length && other_nodes.length) {
26176             nodes= other_nodes;
26177         }
26178         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26179             return false;
26180         }
26181         
26182         return nodes[0];
26183     },
26184     createRange: function(sel)
26185     {
26186         // this has strange effects when using with 
26187         // top toolbar - not sure if it's a great idea.
26188         //this.editor.contentWindow.focus();
26189         if (typeof sel != "undefined") {
26190             try {
26191                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26192             } catch(e) {
26193                 return this.doc.createRange();
26194             }
26195         } else {
26196             return this.doc.createRange();
26197         }
26198     },
26199     getParentElement: function()
26200     {
26201         
26202         this.assignDocWin();
26203         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26204         
26205         var range = this.createRange(sel);
26206          
26207         try {
26208             var p = range.commonAncestorContainer;
26209             while (p.nodeType == 3) { // text node
26210                 p = p.parentNode;
26211             }
26212             return p;
26213         } catch (e) {
26214             return null;
26215         }
26216     
26217     },
26218     /***
26219      *
26220      * Range intersection.. the hard stuff...
26221      *  '-1' = before
26222      *  '0' = hits..
26223      *  '1' = after.
26224      *         [ -- selected range --- ]
26225      *   [fail]                        [fail]
26226      *
26227      *    basically..
26228      *      if end is before start or  hits it. fail.
26229      *      if start is after end or hits it fail.
26230      *
26231      *   if either hits (but other is outside. - then it's not 
26232      *   
26233      *    
26234      **/
26235     
26236     
26237     // @see http://www.thismuchiknow.co.uk/?p=64.
26238     rangeIntersectsNode : function(range, node)
26239     {
26240         var nodeRange = node.ownerDocument.createRange();
26241         try {
26242             nodeRange.selectNode(node);
26243         } catch (e) {
26244             nodeRange.selectNodeContents(node);
26245         }
26246     
26247         var rangeStartRange = range.cloneRange();
26248         rangeStartRange.collapse(true);
26249     
26250         var rangeEndRange = range.cloneRange();
26251         rangeEndRange.collapse(false);
26252     
26253         var nodeStartRange = nodeRange.cloneRange();
26254         nodeStartRange.collapse(true);
26255     
26256         var nodeEndRange = nodeRange.cloneRange();
26257         nodeEndRange.collapse(false);
26258     
26259         return rangeStartRange.compareBoundaryPoints(
26260                  Range.START_TO_START, nodeEndRange) == -1 &&
26261                rangeEndRange.compareBoundaryPoints(
26262                  Range.START_TO_START, nodeStartRange) == 1;
26263         
26264          
26265     },
26266     rangeCompareNode : function(range, node)
26267     {
26268         var nodeRange = node.ownerDocument.createRange();
26269         try {
26270             nodeRange.selectNode(node);
26271         } catch (e) {
26272             nodeRange.selectNodeContents(node);
26273         }
26274         
26275         
26276         range.collapse(true);
26277     
26278         nodeRange.collapse(true);
26279      
26280         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26281         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26282          
26283         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26284         
26285         var nodeIsBefore   =  ss == 1;
26286         var nodeIsAfter    = ee == -1;
26287         
26288         if (nodeIsBefore && nodeIsAfter)
26289             return 0; // outer
26290         if (!nodeIsBefore && nodeIsAfter)
26291             return 1; //right trailed.
26292         
26293         if (nodeIsBefore && !nodeIsAfter)
26294             return 2;  // left trailed.
26295         // fully contined.
26296         return 3;
26297     },
26298
26299     // private? - in a new class?
26300     cleanUpPaste :  function()
26301     {
26302         // cleans up the whole document..
26303          Roo.log('cleanuppaste');
26304         this.cleanUpChildren(this.doc.body);
26305         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26306         if (clean != this.doc.body.innerHTML) {
26307             this.doc.body.innerHTML = clean;
26308         }
26309         
26310     },
26311     
26312     cleanWordChars : function(input) {// change the chars to hex code
26313         var he = Roo.form.HtmlEditor;
26314         
26315         var output = input;
26316         Roo.each(he.swapCodes, function(sw) { 
26317             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26318             
26319             output = output.replace(swapper, sw[1]);
26320         });
26321         
26322         return output;
26323     },
26324     
26325     
26326     cleanUpChildren : function (n)
26327     {
26328         if (!n.childNodes.length) {
26329             return;
26330         }
26331         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26332            this.cleanUpChild(n.childNodes[i]);
26333         }
26334     },
26335     
26336     
26337         
26338     
26339     cleanUpChild : function (node)
26340     {
26341         var ed = this;
26342         //console.log(node);
26343         if (node.nodeName == "#text") {
26344             // clean up silly Windows -- stuff?
26345             return; 
26346         }
26347         if (node.nodeName == "#comment") {
26348             node.parentNode.removeChild(node);
26349             // clean up silly Windows -- stuff?
26350             return; 
26351         }
26352         
26353         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26354             // remove node.
26355             node.parentNode.removeChild(node);
26356             return;
26357             
26358         }
26359         
26360         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26361         
26362         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26363         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26364         
26365         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26366         //    remove_keep_children = true;
26367         //}
26368         
26369         if (remove_keep_children) {
26370             this.cleanUpChildren(node);
26371             // inserts everything just before this node...
26372             while (node.childNodes.length) {
26373                 var cn = node.childNodes[0];
26374                 node.removeChild(cn);
26375                 node.parentNode.insertBefore(cn, node);
26376             }
26377             node.parentNode.removeChild(node);
26378             return;
26379         }
26380         
26381         if (!node.attributes || !node.attributes.length) {
26382             this.cleanUpChildren(node);
26383             return;
26384         }
26385         
26386         function cleanAttr(n,v)
26387         {
26388             
26389             if (v.match(/^\./) || v.match(/^\//)) {
26390                 return;
26391             }
26392             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26393                 return;
26394             }
26395             if (v.match(/^#/)) {
26396                 return;
26397             }
26398 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26399             node.removeAttribute(n);
26400             
26401         }
26402         
26403         function cleanStyle(n,v)
26404         {
26405             if (v.match(/expression/)) { //XSS?? should we even bother..
26406                 node.removeAttribute(n);
26407                 return;
26408             }
26409             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26410             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26411             
26412             
26413             var parts = v.split(/;/);
26414             var clean = [];
26415             
26416             Roo.each(parts, function(p) {
26417                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26418                 if (!p.length) {
26419                     return true;
26420                 }
26421                 var l = p.split(':').shift().replace(/\s+/g,'');
26422                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26423                 
26424                 
26425                 if ( cblack.indexOf(l) > -1) {
26426 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26427                     //node.removeAttribute(n);
26428                     return true;
26429                 }
26430                 //Roo.log()
26431                 // only allow 'c whitelisted system attributes'
26432                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26433 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26434                     //node.removeAttribute(n);
26435                     return true;
26436                 }
26437                 
26438                 
26439                  
26440                 
26441                 clean.push(p);
26442                 return true;
26443             });
26444             if (clean.length) { 
26445                 node.setAttribute(n, clean.join(';'));
26446             } else {
26447                 node.removeAttribute(n);
26448             }
26449             
26450         }
26451         
26452         
26453         for (var i = node.attributes.length-1; i > -1 ; i--) {
26454             var a = node.attributes[i];
26455             //console.log(a);
26456             
26457             if (a.name.toLowerCase().substr(0,2)=='on')  {
26458                 node.removeAttribute(a.name);
26459                 continue;
26460             }
26461             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26462                 node.removeAttribute(a.name);
26463                 continue;
26464             }
26465             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26466                 cleanAttr(a.name,a.value); // fixme..
26467                 continue;
26468             }
26469             if (a.name == 'style') {
26470                 cleanStyle(a.name,a.value);
26471                 continue;
26472             }
26473             /// clean up MS crap..
26474             // tecnically this should be a list of valid class'es..
26475             
26476             
26477             if (a.name == 'class') {
26478                 if (a.value.match(/^Mso/)) {
26479                     node.className = '';
26480                 }
26481                 
26482                 if (a.value.match(/body/)) {
26483                     node.className = '';
26484                 }
26485                 continue;
26486             }
26487             
26488             // style cleanup!?
26489             // class cleanup?
26490             
26491         }
26492         
26493         
26494         this.cleanUpChildren(node);
26495         
26496         
26497     }
26498     
26499     
26500     // hide stuff that is not compatible
26501     /**
26502      * @event blur
26503      * @hide
26504      */
26505     /**
26506      * @event change
26507      * @hide
26508      */
26509     /**
26510      * @event focus
26511      * @hide
26512      */
26513     /**
26514      * @event specialkey
26515      * @hide
26516      */
26517     /**
26518      * @cfg {String} fieldClass @hide
26519      */
26520     /**
26521      * @cfg {String} focusClass @hide
26522      */
26523     /**
26524      * @cfg {String} autoCreate @hide
26525      */
26526     /**
26527      * @cfg {String} inputType @hide
26528      */
26529     /**
26530      * @cfg {String} invalidClass @hide
26531      */
26532     /**
26533      * @cfg {String} invalidText @hide
26534      */
26535     /**
26536      * @cfg {String} msgFx @hide
26537      */
26538     /**
26539      * @cfg {String} validateOnBlur @hide
26540      */
26541 });
26542
26543 Roo.form.HtmlEditor.white = [
26544         'area', 'br', 'img', 'input', 'hr', 'wbr',
26545         
26546        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26547        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26548        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26549        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26550        'table',   'ul',         'xmp', 
26551        
26552        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26553       'thead',   'tr', 
26554      
26555       'dir', 'menu', 'ol', 'ul', 'dl',
26556        
26557       'embed',  'object'
26558 ];
26559
26560
26561 Roo.form.HtmlEditor.black = [
26562     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26563         'applet', // 
26564         'base',   'basefont', 'bgsound', 'blink',  'body', 
26565         'frame',  'frameset', 'head',    'html',   'ilayer', 
26566         'iframe', 'layer',  'link',     'meta',    'object',   
26567         'script', 'style' ,'title',  'xml' // clean later..
26568 ];
26569 Roo.form.HtmlEditor.clean = [
26570     'script', 'style', 'title', 'xml'
26571 ];
26572 Roo.form.HtmlEditor.remove = [
26573     'font'
26574 ];
26575 // attributes..
26576
26577 Roo.form.HtmlEditor.ablack = [
26578     'on'
26579 ];
26580     
26581 Roo.form.HtmlEditor.aclean = [ 
26582     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26583 ];
26584
26585 // protocols..
26586 Roo.form.HtmlEditor.pwhite= [
26587         'http',  'https',  'mailto'
26588 ];
26589
26590 // white listed style attributes.
26591 Roo.form.HtmlEditor.cwhite= [
26592       //  'text-align', /// default is to allow most things..
26593       
26594          
26595 //        'font-size'//??
26596 ];
26597
26598 // black listed style attributes.
26599 Roo.form.HtmlEditor.cblack= [
26600       //  'font-size' -- this can be set by the project 
26601 ];
26602
26603
26604 Roo.form.HtmlEditor.swapCodes   =[ 
26605     [    8211, "--" ], 
26606     [    8212, "--" ], 
26607     [    8216,  "'" ],  
26608     [    8217, "'" ],  
26609     [    8220, '"' ],  
26610     [    8221, '"' ],  
26611     [    8226, "*" ],  
26612     [    8230, "..." ]
26613 ]; 
26614
26615     // <script type="text/javascript">
26616 /*
26617  * Based on
26618  * Ext JS Library 1.1.1
26619  * Copyright(c) 2006-2007, Ext JS, LLC.
26620  *  
26621  
26622  */
26623
26624 /**
26625  * @class Roo.form.HtmlEditorToolbar1
26626  * Basic Toolbar
26627  * 
26628  * Usage:
26629  *
26630  new Roo.form.HtmlEditor({
26631     ....
26632     toolbars : [
26633         new Roo.form.HtmlEditorToolbar1({
26634             disable : { fonts: 1 , format: 1, ..., ... , ...],
26635             btns : [ .... ]
26636         })
26637     }
26638      
26639  * 
26640  * @cfg {Object} disable List of elements to disable..
26641  * @cfg {Array} btns List of additional buttons.
26642  * 
26643  * 
26644  * NEEDS Extra CSS? 
26645  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26646  */
26647  
26648 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26649 {
26650     
26651     Roo.apply(this, config);
26652     
26653     // default disabled, based on 'good practice'..
26654     this.disable = this.disable || {};
26655     Roo.applyIf(this.disable, {
26656         fontSize : true,
26657         colors : true,
26658         specialElements : true
26659     });
26660     
26661     
26662     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26663     // dont call parent... till later.
26664 }
26665
26666 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26667     
26668     tb: false,
26669     
26670     rendered: false,
26671     
26672     editor : false,
26673     /**
26674      * @cfg {Object} disable  List of toolbar elements to disable
26675          
26676      */
26677     disable : false,
26678       /**
26679      * @cfg {Array} fontFamilies An array of available font families
26680      */
26681     fontFamilies : [
26682         'Arial',
26683         'Courier New',
26684         'Tahoma',
26685         'Times New Roman',
26686         'Verdana'
26687     ],
26688     
26689     specialChars : [
26690            "&#169;",
26691           "&#174;",     
26692           "&#8482;",    
26693           "&#163;" ,    
26694          // "&#8212;",    
26695           "&#8230;",    
26696           "&#247;" ,    
26697         //  "&#225;" ,     ?? a acute?
26698            "&#8364;"    , //Euro
26699        //   "&#8220;"    ,
26700         //  "&#8221;"    ,
26701         //  "&#8226;"    ,
26702           "&#176;"  //   , // degrees
26703
26704          // "&#233;"     , // e ecute
26705          // "&#250;"     , // u ecute?
26706     ],
26707     
26708     specialElements : [
26709         {
26710             text: "Insert Table",
26711             xtype: 'MenuItem',
26712             xns : Roo.Menu,
26713             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26714                 
26715         },
26716         {    
26717             text: "Insert Image",
26718             xtype: 'MenuItem',
26719             xns : Roo.Menu,
26720             ihtml : '<img src="about:blank"/>'
26721             
26722         }
26723         
26724          
26725     ],
26726     
26727     
26728     inputElements : [ 
26729             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26730             "input:submit", "input:button", "select", "textarea", "label" ],
26731     formats : [
26732         ["p"] ,  
26733         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26734         ["pre"],[ "code"], 
26735         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26736         ['div'],['span']
26737     ],
26738     
26739     cleanStyles : [
26740         "font-size"
26741     ],
26742      /**
26743      * @cfg {String} defaultFont default font to use.
26744      */
26745     defaultFont: 'tahoma',
26746    
26747     fontSelect : false,
26748     
26749     
26750     formatCombo : false,
26751     
26752     init : function(editor)
26753     {
26754         this.editor = editor;
26755         
26756         
26757         var fid = editor.frameId;
26758         var etb = this;
26759         function btn(id, toggle, handler){
26760             var xid = fid + '-'+ id ;
26761             return {
26762                 id : xid,
26763                 cmd : id,
26764                 cls : 'x-btn-icon x-edit-'+id,
26765                 enableToggle:toggle !== false,
26766                 scope: editor, // was editor...
26767                 handler:handler||editor.relayBtnCmd,
26768                 clickEvent:'mousedown',
26769                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26770                 tabIndex:-1
26771             };
26772         }
26773         
26774         
26775         
26776         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26777         this.tb = tb;
26778          // stop form submits
26779         tb.el.on('click', function(e){
26780             e.preventDefault(); // what does this do?
26781         });
26782
26783         if(!this.disable.font) { // && !Roo.isSafari){
26784             /* why no safari for fonts 
26785             editor.fontSelect = tb.el.createChild({
26786                 tag:'select',
26787                 tabIndex: -1,
26788                 cls:'x-font-select',
26789                 html: this.createFontOptions()
26790             });
26791             
26792             editor.fontSelect.on('change', function(){
26793                 var font = editor.fontSelect.dom.value;
26794                 editor.relayCmd('fontname', font);
26795                 editor.deferFocus();
26796             }, editor);
26797             
26798             tb.add(
26799                 editor.fontSelect.dom,
26800                 '-'
26801             );
26802             */
26803             
26804         };
26805         if(!this.disable.formats){
26806             this.formatCombo = new Roo.form.ComboBox({
26807                 store: new Roo.data.SimpleStore({
26808                     id : 'tag',
26809                     fields: ['tag'],
26810                     data : this.formats // from states.js
26811                 }),
26812                 blockFocus : true,
26813                 name : '',
26814                 //autoCreate : {tag: "div",  size: "20"},
26815                 displayField:'tag',
26816                 typeAhead: false,
26817                 mode: 'local',
26818                 editable : false,
26819                 triggerAction: 'all',
26820                 emptyText:'Add tag',
26821                 selectOnFocus:true,
26822                 width:135,
26823                 listeners : {
26824                     'select': function(c, r, i) {
26825                         editor.insertTag(r.get('tag'));
26826                         editor.focus();
26827                     }
26828                 }
26829
26830             });
26831             tb.addField(this.formatCombo);
26832             
26833         }
26834         
26835         if(!this.disable.format){
26836             tb.add(
26837                 btn('bold'),
26838                 btn('italic'),
26839                 btn('underline')
26840             );
26841         };
26842         if(!this.disable.fontSize){
26843             tb.add(
26844                 '-',
26845                 
26846                 
26847                 btn('increasefontsize', false, editor.adjustFont),
26848                 btn('decreasefontsize', false, editor.adjustFont)
26849             );
26850         };
26851         
26852         
26853         if(!this.disable.colors){
26854             tb.add(
26855                 '-', {
26856                     id:editor.frameId +'-forecolor',
26857                     cls:'x-btn-icon x-edit-forecolor',
26858                     clickEvent:'mousedown',
26859                     tooltip: this.buttonTips['forecolor'] || undefined,
26860                     tabIndex:-1,
26861                     menu : new Roo.menu.ColorMenu({
26862                         allowReselect: true,
26863                         focus: Roo.emptyFn,
26864                         value:'000000',
26865                         plain:true,
26866                         selectHandler: function(cp, color){
26867                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26868                             editor.deferFocus();
26869                         },
26870                         scope: editor,
26871                         clickEvent:'mousedown'
26872                     })
26873                 }, {
26874                     id:editor.frameId +'backcolor',
26875                     cls:'x-btn-icon x-edit-backcolor',
26876                     clickEvent:'mousedown',
26877                     tooltip: this.buttonTips['backcolor'] || undefined,
26878                     tabIndex:-1,
26879                     menu : new Roo.menu.ColorMenu({
26880                         focus: Roo.emptyFn,
26881                         value:'FFFFFF',
26882                         plain:true,
26883                         allowReselect: true,
26884                         selectHandler: function(cp, color){
26885                             if(Roo.isGecko){
26886                                 editor.execCmd('useCSS', false);
26887                                 editor.execCmd('hilitecolor', color);
26888                                 editor.execCmd('useCSS', true);
26889                                 editor.deferFocus();
26890                             }else{
26891                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26892                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26893                                 editor.deferFocus();
26894                             }
26895                         },
26896                         scope:editor,
26897                         clickEvent:'mousedown'
26898                     })
26899                 }
26900             );
26901         };
26902         // now add all the items...
26903         
26904
26905         if(!this.disable.alignments){
26906             tb.add(
26907                 '-',
26908                 btn('justifyleft'),
26909                 btn('justifycenter'),
26910                 btn('justifyright')
26911             );
26912         };
26913
26914         //if(!Roo.isSafari){
26915             if(!this.disable.links){
26916                 tb.add(
26917                     '-',
26918                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26919                 );
26920             };
26921
26922             if(!this.disable.lists){
26923                 tb.add(
26924                     '-',
26925                     btn('insertorderedlist'),
26926                     btn('insertunorderedlist')
26927                 );
26928             }
26929             if(!this.disable.sourceEdit){
26930                 tb.add(
26931                     '-',
26932                     btn('sourceedit', true, function(btn){
26933                         this.toggleSourceEdit(btn.pressed);
26934                     })
26935                 );
26936             }
26937         //}
26938         
26939         var smenu = { };
26940         // special menu.. - needs to be tidied up..
26941         if (!this.disable.special) {
26942             smenu = {
26943                 text: "&#169;",
26944                 cls: 'x-edit-none',
26945                 
26946                 menu : {
26947                     items : []
26948                 }
26949             };
26950             for (var i =0; i < this.specialChars.length; i++) {
26951                 smenu.menu.items.push({
26952                     
26953                     html: this.specialChars[i],
26954                     handler: function(a,b) {
26955                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26956                         //editor.insertAtCursor(a.html);
26957                         
26958                     },
26959                     tabIndex:-1
26960                 });
26961             }
26962             
26963             
26964             tb.add(smenu);
26965             
26966             
26967         }
26968         
26969         var cmenu = { };
26970         if (!this.disable.cleanStyles) {
26971             cmenu = {
26972                 cls: 'x-btn-icon x-btn-clear',
26973                 
26974                 menu : {
26975                     items : []
26976                 }
26977             };
26978             for (var i =0; i < this.cleanStyles.length; i++) {
26979                 cmenu.menu.items.push({
26980                     actiontype : this.cleanStyles[i],
26981                     html: 'Remove ' + this.cleanStyles[i],
26982                     handler: function(a,b) {
26983                         Roo.log(a);
26984                         Roo.log(b);
26985                         var c = Roo.get(editor.doc.body);
26986                         c.select('[style]').each(function(s) {
26987                             s.dom.style.removeProperty(a.actiontype);
26988                         });
26989                         
26990                     },
26991                     tabIndex:-1
26992                 });
26993             }
26994             
26995             tb.add(cmenu);
26996         }
26997          
26998         if (!this.disable.specialElements) {
26999             var semenu = {
27000                 text: "Other;",
27001                 cls: 'x-edit-none',
27002                 menu : {
27003                     items : []
27004                 }
27005             };
27006             for (var i =0; i < this.specialElements.length; i++) {
27007                 semenu.menu.items.push(
27008                     Roo.apply({ 
27009                         handler: function(a,b) {
27010                             editor.insertAtCursor(this.ihtml);
27011                         }
27012                     }, this.specialElements[i])
27013                 );
27014                     
27015             }
27016             
27017             tb.add(semenu);
27018             
27019             
27020         }
27021          
27022         
27023         if (this.btns) {
27024             for(var i =0; i< this.btns.length;i++) {
27025                 var b = Roo.factory(this.btns[i],Roo.form);
27026                 b.cls =  'x-edit-none';
27027                 b.scope = editor;
27028                 tb.add(b);
27029             }
27030         
27031         }
27032         
27033         
27034         
27035         // disable everything...
27036         
27037         this.tb.items.each(function(item){
27038            if(item.id != editor.frameId+ '-sourceedit'){
27039                 item.disable();
27040             }
27041         });
27042         this.rendered = true;
27043         
27044         // the all the btns;
27045         editor.on('editorevent', this.updateToolbar, this);
27046         // other toolbars need to implement this..
27047         //editor.on('editmodechange', this.updateToolbar, this);
27048     },
27049     
27050     
27051     
27052     /**
27053      * Protected method that will not generally be called directly. It triggers
27054      * a toolbar update by reading the markup state of the current selection in the editor.
27055      */
27056     updateToolbar: function(){
27057
27058         if(!this.editor.activated){
27059             this.editor.onFirstFocus();
27060             return;
27061         }
27062
27063         var btns = this.tb.items.map, 
27064             doc = this.editor.doc,
27065             frameId = this.editor.frameId;
27066
27067         if(!this.disable.font && !Roo.isSafari){
27068             /*
27069             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27070             if(name != this.fontSelect.dom.value){
27071                 this.fontSelect.dom.value = name;
27072             }
27073             */
27074         }
27075         if(!this.disable.format){
27076             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27077             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27078             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27079         }
27080         if(!this.disable.alignments){
27081             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27082             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27083             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27084         }
27085         if(!Roo.isSafari && !this.disable.lists){
27086             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27087             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27088         }
27089         
27090         var ans = this.editor.getAllAncestors();
27091         if (this.formatCombo) {
27092             
27093             
27094             var store = this.formatCombo.store;
27095             this.formatCombo.setValue("");
27096             for (var i =0; i < ans.length;i++) {
27097                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27098                     // select it..
27099                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27100                     break;
27101                 }
27102             }
27103         }
27104         
27105         
27106         
27107         // hides menus... - so this cant be on a menu...
27108         Roo.menu.MenuMgr.hideAll();
27109
27110         //this.editorsyncValue();
27111     },
27112    
27113     
27114     createFontOptions : function(){
27115         var buf = [], fs = this.fontFamilies, ff, lc;
27116         
27117         
27118         
27119         for(var i = 0, len = fs.length; i< len; i++){
27120             ff = fs[i];
27121             lc = ff.toLowerCase();
27122             buf.push(
27123                 '<option value="',lc,'" style="font-family:',ff,';"',
27124                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27125                     ff,
27126                 '</option>'
27127             );
27128         }
27129         return buf.join('');
27130     },
27131     
27132     toggleSourceEdit : function(sourceEditMode){
27133         if(sourceEditMode === undefined){
27134             sourceEditMode = !this.sourceEditMode;
27135         }
27136         this.sourceEditMode = sourceEditMode === true;
27137         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27138         // just toggle the button?
27139         if(btn.pressed !== this.editor.sourceEditMode){
27140             btn.toggle(this.editor.sourceEditMode);
27141             return;
27142         }
27143         
27144         if(this.sourceEditMode){
27145             this.tb.items.each(function(item){
27146                 if(item.cmd != 'sourceedit'){
27147                     item.disable();
27148                 }
27149             });
27150           
27151         }else{
27152             if(this.initialized){
27153                 this.tb.items.each(function(item){
27154                     item.enable();
27155                 });
27156             }
27157             
27158         }
27159         // tell the editor that it's been pressed..
27160         this.editor.toggleSourceEdit(sourceEditMode);
27161        
27162     },
27163      /**
27164      * Object collection of toolbar tooltips for the buttons in the editor. The key
27165      * is the command id associated with that button and the value is a valid QuickTips object.
27166      * For example:
27167 <pre><code>
27168 {
27169     bold : {
27170         title: 'Bold (Ctrl+B)',
27171         text: 'Make the selected text bold.',
27172         cls: 'x-html-editor-tip'
27173     },
27174     italic : {
27175         title: 'Italic (Ctrl+I)',
27176         text: 'Make the selected text italic.',
27177         cls: 'x-html-editor-tip'
27178     },
27179     ...
27180 </code></pre>
27181     * @type Object
27182      */
27183     buttonTips : {
27184         bold : {
27185             title: 'Bold (Ctrl+B)',
27186             text: 'Make the selected text bold.',
27187             cls: 'x-html-editor-tip'
27188         },
27189         italic : {
27190             title: 'Italic (Ctrl+I)',
27191             text: 'Make the selected text italic.',
27192             cls: 'x-html-editor-tip'
27193         },
27194         underline : {
27195             title: 'Underline (Ctrl+U)',
27196             text: 'Underline the selected text.',
27197             cls: 'x-html-editor-tip'
27198         },
27199         increasefontsize : {
27200             title: 'Grow Text',
27201             text: 'Increase the font size.',
27202             cls: 'x-html-editor-tip'
27203         },
27204         decreasefontsize : {
27205             title: 'Shrink Text',
27206             text: 'Decrease the font size.',
27207             cls: 'x-html-editor-tip'
27208         },
27209         backcolor : {
27210             title: 'Text Highlight Color',
27211             text: 'Change the background color of the selected text.',
27212             cls: 'x-html-editor-tip'
27213         },
27214         forecolor : {
27215             title: 'Font Color',
27216             text: 'Change the color of the selected text.',
27217             cls: 'x-html-editor-tip'
27218         },
27219         justifyleft : {
27220             title: 'Align Text Left',
27221             text: 'Align text to the left.',
27222             cls: 'x-html-editor-tip'
27223         },
27224         justifycenter : {
27225             title: 'Center Text',
27226             text: 'Center text in the editor.',
27227             cls: 'x-html-editor-tip'
27228         },
27229         justifyright : {
27230             title: 'Align Text Right',
27231             text: 'Align text to the right.',
27232             cls: 'x-html-editor-tip'
27233         },
27234         insertunorderedlist : {
27235             title: 'Bullet List',
27236             text: 'Start a bulleted list.',
27237             cls: 'x-html-editor-tip'
27238         },
27239         insertorderedlist : {
27240             title: 'Numbered List',
27241             text: 'Start a numbered list.',
27242             cls: 'x-html-editor-tip'
27243         },
27244         createlink : {
27245             title: 'Hyperlink',
27246             text: 'Make the selected text a hyperlink.',
27247             cls: 'x-html-editor-tip'
27248         },
27249         sourceedit : {
27250             title: 'Source Edit',
27251             text: 'Switch to source editing mode.',
27252             cls: 'x-html-editor-tip'
27253         }
27254     },
27255     // private
27256     onDestroy : function(){
27257         if(this.rendered){
27258             
27259             this.tb.items.each(function(item){
27260                 if(item.menu){
27261                     item.menu.removeAll();
27262                     if(item.menu.el){
27263                         item.menu.el.destroy();
27264                     }
27265                 }
27266                 item.destroy();
27267             });
27268              
27269         }
27270     },
27271     onFirstFocus: function() {
27272         this.tb.items.each(function(item){
27273            item.enable();
27274         });
27275     }
27276 });
27277
27278
27279
27280
27281 // <script type="text/javascript">
27282 /*
27283  * Based on
27284  * Ext JS Library 1.1.1
27285  * Copyright(c) 2006-2007, Ext JS, LLC.
27286  *  
27287  
27288  */
27289
27290  
27291 /**
27292  * @class Roo.form.HtmlEditor.ToolbarContext
27293  * Context Toolbar
27294  * 
27295  * Usage:
27296  *
27297  new Roo.form.HtmlEditor({
27298     ....
27299     toolbars : [
27300         { xtype: 'ToolbarStandard', styles : {} }
27301         { xtype: 'ToolbarContext', disable : {} }
27302     ]
27303 })
27304
27305      
27306  * 
27307  * @config : {Object} disable List of elements to disable.. (not done yet.)
27308  * @config : {Object} styles  Map of styles available.
27309  * 
27310  */
27311
27312 Roo.form.HtmlEditor.ToolbarContext = function(config)
27313 {
27314     
27315     Roo.apply(this, config);
27316     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27317     // dont call parent... till later.
27318     this.styles = this.styles || {};
27319 }
27320
27321  
27322
27323 Roo.form.HtmlEditor.ToolbarContext.types = {
27324     'IMG' : {
27325         width : {
27326             title: "Width",
27327             width: 40
27328         },
27329         height:  {
27330             title: "Height",
27331             width: 40
27332         },
27333         align: {
27334             title: "Align",
27335             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27336             width : 80
27337             
27338         },
27339         border: {
27340             title: "Border",
27341             width: 40
27342         },
27343         alt: {
27344             title: "Alt",
27345             width: 120
27346         },
27347         src : {
27348             title: "Src",
27349             width: 220
27350         }
27351         
27352     },
27353     'A' : {
27354         name : {
27355             title: "Name",
27356             width: 50
27357         },
27358         target:  {
27359             title: "Target",
27360             width: 120
27361         },
27362         href:  {
27363             title: "Href",
27364             width: 220
27365         } // border?
27366         
27367     },
27368     'TABLE' : {
27369         rows : {
27370             title: "Rows",
27371             width: 20
27372         },
27373         cols : {
27374             title: "Cols",
27375             width: 20
27376         },
27377         width : {
27378             title: "Width",
27379             width: 40
27380         },
27381         height : {
27382             title: "Height",
27383             width: 40
27384         },
27385         border : {
27386             title: "Border",
27387             width: 20
27388         }
27389     },
27390     'TD' : {
27391         width : {
27392             title: "Width",
27393             width: 40
27394         },
27395         height : {
27396             title: "Height",
27397             width: 40
27398         },   
27399         align: {
27400             title: "Align",
27401             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27402             width: 80
27403         },
27404         valign: {
27405             title: "Valign",
27406             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27407             width: 80
27408         },
27409         colspan: {
27410             title: "Colspan",
27411             width: 20
27412             
27413         },
27414          'font-family'  : {
27415             title : "Font",
27416             style : 'fontFamily',
27417             displayField: 'display',
27418             optname : 'font-family',
27419             width: 140
27420         }
27421     },
27422     'INPUT' : {
27423         name : {
27424             title: "name",
27425             width: 120
27426         },
27427         value : {
27428             title: "Value",
27429             width: 120
27430         },
27431         width : {
27432             title: "Width",
27433             width: 40
27434         }
27435     },
27436     'LABEL' : {
27437         'for' : {
27438             title: "For",
27439             width: 120
27440         }
27441     },
27442     'TEXTAREA' : {
27443           name : {
27444             title: "name",
27445             width: 120
27446         },
27447         rows : {
27448             title: "Rows",
27449             width: 20
27450         },
27451         cols : {
27452             title: "Cols",
27453             width: 20
27454         }
27455     },
27456     'SELECT' : {
27457         name : {
27458             title: "name",
27459             width: 120
27460         },
27461         selectoptions : {
27462             title: "Options",
27463             width: 200
27464         }
27465     },
27466     
27467     // should we really allow this??
27468     // should this just be 
27469     'BODY' : {
27470         title : {
27471             title: "Title",
27472             width: 200,
27473             disabled : true
27474         }
27475     },
27476     'SPAN' : {
27477         'font-family'  : {
27478             title : "Font",
27479             style : 'fontFamily',
27480             displayField: 'display',
27481             optname : 'font-family',
27482             width: 140
27483         }
27484     },
27485     'DIV' : {
27486         'font-family'  : {
27487             title : "Font",
27488             style : 'fontFamily',
27489             displayField: 'display',
27490             optname : 'font-family',
27491             width: 140
27492         }
27493     },
27494      'P' : {
27495         'font-family'  : {
27496             title : "Font",
27497             style : 'fontFamily',
27498             displayField: 'display',
27499             optname : 'font-family',
27500             width: 140
27501         }
27502     },
27503     
27504     '*' : {
27505         // empty..
27506     }
27507
27508 };
27509
27510 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27511 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27512
27513 Roo.form.HtmlEditor.ToolbarContext.options = {
27514         'font-family'  : [ 
27515                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27516                 [ 'Courier New', 'Courier New'],
27517                 [ 'Tahoma', 'Tahoma'],
27518                 [ 'Times New Roman,serif', 'Times'],
27519                 [ 'Verdana','Verdana' ]
27520         ]
27521 };
27522
27523 // fixme - these need to be configurable..
27524  
27525
27526 Roo.form.HtmlEditor.ToolbarContext.types
27527
27528
27529 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27530     
27531     tb: false,
27532     
27533     rendered: false,
27534     
27535     editor : false,
27536     /**
27537      * @cfg {Object} disable  List of toolbar elements to disable
27538          
27539      */
27540     disable : false,
27541     /**
27542      * @cfg {Object} styles List of styles 
27543      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27544      *
27545      * These must be defined in the page, so they get rendered correctly..
27546      * .headline { }
27547      * TD.underline { }
27548      * 
27549      */
27550     styles : false,
27551     
27552     options: false,
27553     
27554     toolbars : false,
27555     
27556     init : function(editor)
27557     {
27558         this.editor = editor;
27559         
27560         
27561         var fid = editor.frameId;
27562         var etb = this;
27563         function btn(id, toggle, handler){
27564             var xid = fid + '-'+ id ;
27565             return {
27566                 id : xid,
27567                 cmd : id,
27568                 cls : 'x-btn-icon x-edit-'+id,
27569                 enableToggle:toggle !== false,
27570                 scope: editor, // was editor...
27571                 handler:handler||editor.relayBtnCmd,
27572                 clickEvent:'mousedown',
27573                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27574                 tabIndex:-1
27575             };
27576         }
27577         // create a new element.
27578         var wdiv = editor.wrap.createChild({
27579                 tag: 'div'
27580             }, editor.wrap.dom.firstChild.nextSibling, true);
27581         
27582         // can we do this more than once??
27583         
27584          // stop form submits
27585       
27586  
27587         // disable everything...
27588         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27589         this.toolbars = {};
27590            
27591         for (var i in  ty) {
27592           
27593             this.toolbars[i] = this.buildToolbar(ty[i],i);
27594         }
27595         this.tb = this.toolbars.BODY;
27596         this.tb.el.show();
27597         this.buildFooter();
27598         this.footer.show();
27599         editor.on('hide', function( ) { this.footer.hide() }, this);
27600         editor.on('show', function( ) { this.footer.show() }, this);
27601         
27602          
27603         this.rendered = true;
27604         
27605         // the all the btns;
27606         editor.on('editorevent', this.updateToolbar, this);
27607         // other toolbars need to implement this..
27608         //editor.on('editmodechange', this.updateToolbar, this);
27609     },
27610     
27611     
27612     
27613     /**
27614      * Protected method that will not generally be called directly. It triggers
27615      * a toolbar update by reading the markup state of the current selection in the editor.
27616      */
27617     updateToolbar: function(editor,ev,sel){
27618
27619         //Roo.log(ev);
27620         // capture mouse up - this is handy for selecting images..
27621         // perhaps should go somewhere else...
27622         if(!this.editor.activated){
27623              this.editor.onFirstFocus();
27624             return;
27625         }
27626         
27627         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27628         // selectNode - might want to handle IE?
27629         if (ev &&
27630             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27631             ev.target && ev.target.tagName == 'IMG') {
27632             // they have click on an image...
27633             // let's see if we can change the selection...
27634             sel = ev.target;
27635          
27636               var nodeRange = sel.ownerDocument.createRange();
27637             try {
27638                 nodeRange.selectNode(sel);
27639             } catch (e) {
27640                 nodeRange.selectNodeContents(sel);
27641             }
27642             //nodeRange.collapse(true);
27643             var s = editor.win.getSelection();
27644             s.removeAllRanges();
27645             s.addRange(nodeRange);
27646         }  
27647         
27648       
27649         var updateFooter = sel ? false : true;
27650         
27651         
27652         var ans = this.editor.getAllAncestors();
27653         
27654         // pick
27655         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27656         
27657         if (!sel) { 
27658             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27659             sel = sel ? sel : this.editor.doc.body;
27660             sel = sel.tagName.length ? sel : this.editor.doc.body;
27661             
27662         }
27663         // pick a menu that exists..
27664         var tn = sel.tagName.toUpperCase();
27665         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27666         
27667         tn = sel.tagName.toUpperCase();
27668         
27669         var lastSel = this.tb.selectedNode
27670         
27671         this.tb.selectedNode = sel;
27672         
27673         // if current menu does not match..
27674         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27675                 
27676             this.tb.el.hide();
27677             ///console.log("show: " + tn);
27678             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27679             this.tb.el.show();
27680             // update name
27681             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27682             
27683             
27684             // update attributes
27685             if (this.tb.fields) {
27686                 this.tb.fields.each(function(e) {
27687                     if (e.stylename) {
27688                         e.setValue(sel.style[e.stylename]);
27689                         return;
27690                     } 
27691                    e.setValue(sel.getAttribute(e.attrname));
27692                 });
27693             }
27694             
27695             var hasStyles = false;
27696             for(var i in this.styles) {
27697                 hasStyles = true;
27698                 break;
27699             }
27700             
27701             // update styles
27702             if (hasStyles) { 
27703                 var st = this.tb.fields.item(0);
27704                 
27705                 st.store.removeAll();
27706                
27707                 
27708                 var cn = sel.className.split(/\s+/);
27709                 
27710                 var avs = [];
27711                 if (this.styles['*']) {
27712                     
27713                     Roo.each(this.styles['*'], function(v) {
27714                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27715                     });
27716                 }
27717                 if (this.styles[tn]) { 
27718                     Roo.each(this.styles[tn], function(v) {
27719                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27720                     });
27721                 }
27722                 
27723                 st.store.loadData(avs);
27724                 st.collapse();
27725                 st.setValue(cn);
27726             }
27727             // flag our selected Node.
27728             this.tb.selectedNode = sel;
27729            
27730            
27731             Roo.menu.MenuMgr.hideAll();
27732
27733         }
27734         
27735         if (!updateFooter) {
27736             //this.footDisp.dom.innerHTML = ''; 
27737             return;
27738         }
27739         // update the footer
27740         //
27741         var html = '';
27742         
27743         this.footerEls = ans.reverse();
27744         Roo.each(this.footerEls, function(a,i) {
27745             if (!a) { return; }
27746             html += html.length ? ' &gt; '  :  '';
27747             
27748             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27749             
27750         });
27751        
27752         // 
27753         var sz = this.footDisp.up('td').getSize();
27754         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27755         this.footDisp.dom.style.marginLeft = '5px';
27756         
27757         this.footDisp.dom.style.overflow = 'hidden';
27758         
27759         this.footDisp.dom.innerHTML = html;
27760             
27761         //this.editorsyncValue();
27762     },
27763      
27764     
27765    
27766        
27767     // private
27768     onDestroy : function(){
27769         if(this.rendered){
27770             
27771             this.tb.items.each(function(item){
27772                 if(item.menu){
27773                     item.menu.removeAll();
27774                     if(item.menu.el){
27775                         item.menu.el.destroy();
27776                     }
27777                 }
27778                 item.destroy();
27779             });
27780              
27781         }
27782     },
27783     onFirstFocus: function() {
27784         // need to do this for all the toolbars..
27785         this.tb.items.each(function(item){
27786            item.enable();
27787         });
27788     },
27789     buildToolbar: function(tlist, nm)
27790     {
27791         var editor = this.editor;
27792          // create a new element.
27793         var wdiv = editor.wrap.createChild({
27794                 tag: 'div'
27795             }, editor.wrap.dom.firstChild.nextSibling, true);
27796         
27797        
27798         var tb = new Roo.Toolbar(wdiv);
27799         // add the name..
27800         
27801         tb.add(nm+ ":&nbsp;");
27802         
27803         var styles = [];
27804         for(var i in this.styles) {
27805             styles.push(i);
27806         }
27807         
27808         // styles...
27809         if (styles && styles.length) {
27810             
27811             // this needs a multi-select checkbox...
27812             tb.addField( new Roo.form.ComboBox({
27813                 store: new Roo.data.SimpleStore({
27814                     id : 'val',
27815                     fields: ['val', 'selected'],
27816                     data : [] 
27817                 }),
27818                 name : '-roo-edit-className',
27819                 attrname : 'className',
27820                 displayField: 'val',
27821                 typeAhead: false,
27822                 mode: 'local',
27823                 editable : false,
27824                 triggerAction: 'all',
27825                 emptyText:'Select Style',
27826                 selectOnFocus:true,
27827                 width: 130,
27828                 listeners : {
27829                     'select': function(c, r, i) {
27830                         // initial support only for on class per el..
27831                         tb.selectedNode.className =  r ? r.get('val') : '';
27832                         editor.syncValue();
27833                     }
27834                 }
27835     
27836             }));
27837         }
27838         
27839         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27840         var tbops = tbc.options;
27841         
27842         for (var i in tlist) {
27843             
27844             var item = tlist[i];
27845             tb.add(item.title + ":&nbsp;");
27846             
27847             
27848             //optname == used so you can configure the options available..
27849             var opts = item.opts ? item.opts : false;
27850             if (item.optname) {
27851                 opts = tbops[item.optname];
27852            
27853             }
27854             
27855             if (opts) {
27856                 // opts == pulldown..
27857                 tb.addField( new Roo.form.ComboBox({
27858                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27859                         id : 'val',
27860                         fields: ['val', 'display'],
27861                         data : opts  
27862                     }),
27863                     name : '-roo-edit-' + i,
27864                     attrname : i,
27865                     stylename : item.style ? item.style : false,
27866                     displayField: item.displayField ? item.displayField : 'val',
27867                     valueField :  'val',
27868                     typeAhead: false,
27869                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27870                     editable : false,
27871                     triggerAction: 'all',
27872                     emptyText:'Select',
27873                     selectOnFocus:true,
27874                     width: item.width ? item.width  : 130,
27875                     listeners : {
27876                         'select': function(c, r, i) {
27877                             if (c.stylename) {
27878                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27879                                 return;
27880                             }
27881                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27882                         }
27883                     }
27884
27885                 }));
27886                 continue;
27887                     
27888                  
27889                 
27890                 tb.addField( new Roo.form.TextField({
27891                     name: i,
27892                     width: 100,
27893                     //allowBlank:false,
27894                     value: ''
27895                 }));
27896                 continue;
27897             }
27898             tb.addField( new Roo.form.TextField({
27899                 name: '-roo-edit-' + i,
27900                 attrname : i,
27901                 
27902                 width: item.width,
27903                 //allowBlank:true,
27904                 value: '',
27905                 listeners: {
27906                     'change' : function(f, nv, ov) {
27907                         tb.selectedNode.setAttribute(f.attrname, nv);
27908                     }
27909                 }
27910             }));
27911              
27912         }
27913         tb.addFill();
27914         var _this = this;
27915         tb.addButton( {
27916             text: 'Remove Tag',
27917     
27918             listeners : {
27919                 click : function ()
27920                 {
27921                     // remove
27922                     // undo does not work.
27923                      
27924                     var sn = tb.selectedNode;
27925                     
27926                     var pn = sn.parentNode;
27927                     
27928                     var stn =  sn.childNodes[0];
27929                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27930                     while (sn.childNodes.length) {
27931                         var node = sn.childNodes[0];
27932                         sn.removeChild(node);
27933                         //Roo.log(node);
27934                         pn.insertBefore(node, sn);
27935                         
27936                     }
27937                     pn.removeChild(sn);
27938                     var range = editor.createRange();
27939         
27940                     range.setStart(stn,0);
27941                     range.setEnd(en,0); //????
27942                     //range.selectNode(sel);
27943                     
27944                     
27945                     var selection = editor.getSelection();
27946                     selection.removeAllRanges();
27947                     selection.addRange(range);
27948                     
27949                     
27950                     
27951                     //_this.updateToolbar(null, null, pn);
27952                     _this.updateToolbar(null, null, null);
27953                     _this.footDisp.dom.innerHTML = ''; 
27954                 }
27955             }
27956             
27957                     
27958                 
27959             
27960         });
27961         
27962         
27963         tb.el.on('click', function(e){
27964             e.preventDefault(); // what does this do?
27965         });
27966         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27967         tb.el.hide();
27968         tb.name = nm;
27969         // dont need to disable them... as they will get hidden
27970         return tb;
27971          
27972         
27973     },
27974     buildFooter : function()
27975     {
27976         
27977         var fel = this.editor.wrap.createChild();
27978         this.footer = new Roo.Toolbar(fel);
27979         // toolbar has scrolly on left / right?
27980         var footDisp= new Roo.Toolbar.Fill();
27981         var _t = this;
27982         this.footer.add(
27983             {
27984                 text : '&lt;',
27985                 xtype: 'Button',
27986                 handler : function() {
27987                     _t.footDisp.scrollTo('left',0,true)
27988                 }
27989             }
27990         );
27991         this.footer.add( footDisp );
27992         this.footer.add( 
27993             {
27994                 text : '&gt;',
27995                 xtype: 'Button',
27996                 handler : function() {
27997                     // no animation..
27998                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27999                 }
28000             }
28001         );
28002         var fel = Roo.get(footDisp.el);
28003         fel.addClass('x-editor-context');
28004         this.footDispWrap = fel; 
28005         this.footDispWrap.overflow  = 'hidden';
28006         
28007         this.footDisp = fel.createChild();
28008         this.footDispWrap.on('click', this.onContextClick, this)
28009         
28010         
28011     },
28012     onContextClick : function (ev,dom)
28013     {
28014         ev.preventDefault();
28015         var  cn = dom.className;
28016         //Roo.log(cn);
28017         if (!cn.match(/x-ed-loc-/)) {
28018             return;
28019         }
28020         var n = cn.split('-').pop();
28021         var ans = this.footerEls;
28022         var sel = ans[n];
28023         
28024          // pick
28025         var range = this.editor.createRange();
28026         
28027         range.selectNodeContents(sel);
28028         //range.selectNode(sel);
28029         
28030         
28031         var selection = this.editor.getSelection();
28032         selection.removeAllRanges();
28033         selection.addRange(range);
28034         
28035         
28036         
28037         this.updateToolbar(null, null, sel);
28038         
28039         
28040     }
28041     
28042     
28043     
28044     
28045     
28046 });
28047
28048
28049
28050
28051
28052 /*
28053  * Based on:
28054  * Ext JS Library 1.1.1
28055  * Copyright(c) 2006-2007, Ext JS, LLC.
28056  *
28057  * Originally Released Under LGPL - original licence link has changed is not relivant.
28058  *
28059  * Fork - LGPL
28060  * <script type="text/javascript">
28061  */
28062  
28063 /**
28064  * @class Roo.form.BasicForm
28065  * @extends Roo.util.Observable
28066  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28067  * @constructor
28068  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28069  * @param {Object} config Configuration options
28070  */
28071 Roo.form.BasicForm = function(el, config){
28072     this.allItems = [];
28073     this.childForms = [];
28074     Roo.apply(this, config);
28075     /*
28076      * The Roo.form.Field items in this form.
28077      * @type MixedCollection
28078      */
28079      
28080      
28081     this.items = new Roo.util.MixedCollection(false, function(o){
28082         return o.id || (o.id = Roo.id());
28083     });
28084     this.addEvents({
28085         /**
28086          * @event beforeaction
28087          * Fires before any action is performed. Return false to cancel the action.
28088          * @param {Form} this
28089          * @param {Action} action The action to be performed
28090          */
28091         beforeaction: true,
28092         /**
28093          * @event actionfailed
28094          * Fires when an action fails.
28095          * @param {Form} this
28096          * @param {Action} action The action that failed
28097          */
28098         actionfailed : true,
28099         /**
28100          * @event actioncomplete
28101          * Fires when an action is completed.
28102          * @param {Form} this
28103          * @param {Action} action The action that completed
28104          */
28105         actioncomplete : true
28106     });
28107     if(el){
28108         this.initEl(el);
28109     }
28110     Roo.form.BasicForm.superclass.constructor.call(this);
28111 };
28112
28113 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28114     /**
28115      * @cfg {String} method
28116      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28117      */
28118     /**
28119      * @cfg {DataReader} reader
28120      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28121      * This is optional as there is built-in support for processing JSON.
28122      */
28123     /**
28124      * @cfg {DataReader} errorReader
28125      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28126      * This is completely optional as there is built-in support for processing JSON.
28127      */
28128     /**
28129      * @cfg {String} url
28130      * The URL to use for form actions if one isn't supplied in the action options.
28131      */
28132     /**
28133      * @cfg {Boolean} fileUpload
28134      * Set to true if this form is a file upload.
28135      */
28136      
28137     /**
28138      * @cfg {Object} baseParams
28139      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28140      */
28141      /**
28142      
28143     /**
28144      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28145      */
28146     timeout: 30,
28147
28148     // private
28149     activeAction : null,
28150
28151     /**
28152      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28153      * or setValues() data instead of when the form was first created.
28154      */
28155     trackResetOnLoad : false,
28156     
28157     
28158     /**
28159      * childForms - used for multi-tab forms
28160      * @type {Array}
28161      */
28162     childForms : false,
28163     
28164     /**
28165      * allItems - full list of fields.
28166      * @type {Array}
28167      */
28168     allItems : false,
28169     
28170     /**
28171      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28172      * element by passing it or its id or mask the form itself by passing in true.
28173      * @type Mixed
28174      */
28175     waitMsgTarget : false,
28176
28177     // private
28178     initEl : function(el){
28179         this.el = Roo.get(el);
28180         this.id = this.el.id || Roo.id();
28181         this.el.on('submit', this.onSubmit, this);
28182         this.el.addClass('x-form');
28183     },
28184
28185     // private
28186     onSubmit : function(e){
28187         e.stopEvent();
28188     },
28189
28190     /**
28191      * Returns true if client-side validation on the form is successful.
28192      * @return Boolean
28193      */
28194     isValid : function(){
28195         var valid = true;
28196         this.items.each(function(f){
28197            if(!f.validate()){
28198                valid = false;
28199            }
28200         });
28201         return valid;
28202     },
28203
28204     /**
28205      * Returns true if any fields in this form have changed since their original load.
28206      * @return Boolean
28207      */
28208     isDirty : function(){
28209         var dirty = false;
28210         this.items.each(function(f){
28211            if(f.isDirty()){
28212                dirty = true;
28213                return false;
28214            }
28215         });
28216         return dirty;
28217     },
28218
28219     /**
28220      * Performs a predefined action (submit or load) or custom actions you define on this form.
28221      * @param {String} actionName The name of the action type
28222      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28223      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28224      * accept other config options):
28225      * <pre>
28226 Property          Type             Description
28227 ----------------  ---------------  ----------------------------------------------------------------------------------
28228 url               String           The url for the action (defaults to the form's url)
28229 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28230 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28231 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28232                                    validate the form on the client (defaults to false)
28233      * </pre>
28234      * @return {BasicForm} this
28235      */
28236     doAction : function(action, options){
28237         if(typeof action == 'string'){
28238             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28239         }
28240         if(this.fireEvent('beforeaction', this, action) !== false){
28241             this.beforeAction(action);
28242             action.run.defer(100, action);
28243         }
28244         return this;
28245     },
28246
28247     /**
28248      * Shortcut to do a submit action.
28249      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28250      * @return {BasicForm} this
28251      */
28252     submit : function(options){
28253         this.doAction('submit', options);
28254         return this;
28255     },
28256
28257     /**
28258      * Shortcut to do a load action.
28259      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28260      * @return {BasicForm} this
28261      */
28262     load : function(options){
28263         this.doAction('load', options);
28264         return this;
28265     },
28266
28267     /**
28268      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28269      * @param {Record} record The record to edit
28270      * @return {BasicForm} this
28271      */
28272     updateRecord : function(record){
28273         record.beginEdit();
28274         var fs = record.fields;
28275         fs.each(function(f){
28276             var field = this.findField(f.name);
28277             if(field){
28278                 record.set(f.name, field.getValue());
28279             }
28280         }, this);
28281         record.endEdit();
28282         return this;
28283     },
28284
28285     /**
28286      * Loads an Roo.data.Record into this form.
28287      * @param {Record} record The record to load
28288      * @return {BasicForm} this
28289      */
28290     loadRecord : function(record){
28291         this.setValues(record.data);
28292         return this;
28293     },
28294
28295     // private
28296     beforeAction : function(action){
28297         var o = action.options;
28298         
28299        
28300         if(this.waitMsgTarget === true){
28301             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28302         }else if(this.waitMsgTarget){
28303             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28304             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28305         }else {
28306             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28307         }
28308          
28309     },
28310
28311     // private
28312     afterAction : function(action, success){
28313         this.activeAction = null;
28314         var o = action.options;
28315         
28316         if(this.waitMsgTarget === true){
28317             this.el.unmask();
28318         }else if(this.waitMsgTarget){
28319             this.waitMsgTarget.unmask();
28320         }else{
28321             Roo.MessageBox.updateProgress(1);
28322             Roo.MessageBox.hide();
28323         }
28324          
28325         if(success){
28326             if(o.reset){
28327                 this.reset();
28328             }
28329             Roo.callback(o.success, o.scope, [this, action]);
28330             this.fireEvent('actioncomplete', this, action);
28331             
28332         }else{
28333             
28334             // failure condition..
28335             // we have a scenario where updates need confirming.
28336             // eg. if a locking scenario exists..
28337             // we look for { errors : { needs_confirm : true }} in the response.
28338             if (
28339                 (typeof(action.result) != 'undefined')  &&
28340                 (typeof(action.result.errors) != 'undefined')  &&
28341                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28342            ){
28343                 var _t = this;
28344                 Roo.MessageBox.confirm(
28345                     "Change requires confirmation",
28346                     action.result.errorMsg,
28347                     function(r) {
28348                         if (r != 'yes') {
28349                             return;
28350                         }
28351                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28352                     }
28353                     
28354                 );
28355                 
28356                 
28357                 
28358                 return;
28359             }
28360             
28361             Roo.callback(o.failure, o.scope, [this, action]);
28362             // show an error message if no failed handler is set..
28363             if (!this.hasListener('actionfailed')) {
28364                 Roo.MessageBox.alert("Error",
28365                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28366                         action.result.errorMsg :
28367                         "Saving Failed, please check your entries or try again"
28368                 );
28369             }
28370             
28371             this.fireEvent('actionfailed', this, action);
28372         }
28373         
28374     },
28375
28376     /**
28377      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28378      * @param {String} id The value to search for
28379      * @return Field
28380      */
28381     findField : function(id){
28382         var field = this.items.get(id);
28383         if(!field){
28384             this.items.each(function(f){
28385                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28386                     field = f;
28387                     return false;
28388                 }
28389             });
28390         }
28391         return field || null;
28392     },
28393
28394     /**
28395      * Add a secondary form to this one, 
28396      * Used to provide tabbed forms. One form is primary, with hidden values 
28397      * which mirror the elements from the other forms.
28398      * 
28399      * @param {Roo.form.Form} form to add.
28400      * 
28401      */
28402     addForm : function(form)
28403     {
28404        
28405         if (this.childForms.indexOf(form) > -1) {
28406             // already added..
28407             return;
28408         }
28409         this.childForms.push(form);
28410         var n = '';
28411         Roo.each(form.allItems, function (fe) {
28412             
28413             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28414             if (this.findField(n)) { // already added..
28415                 return;
28416             }
28417             var add = new Roo.form.Hidden({
28418                 name : n
28419             });
28420             add.render(this.el);
28421             
28422             this.add( add );
28423         }, this);
28424         
28425     },
28426     /**
28427      * Mark fields in this form invalid in bulk.
28428      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28429      * @return {BasicForm} this
28430      */
28431     markInvalid : function(errors){
28432         if(errors instanceof Array){
28433             for(var i = 0, len = errors.length; i < len; i++){
28434                 var fieldError = errors[i];
28435                 var f = this.findField(fieldError.id);
28436                 if(f){
28437                     f.markInvalid(fieldError.msg);
28438                 }
28439             }
28440         }else{
28441             var field, id;
28442             for(id in errors){
28443                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28444                     field.markInvalid(errors[id]);
28445                 }
28446             }
28447         }
28448         Roo.each(this.childForms || [], function (f) {
28449             f.markInvalid(errors);
28450         });
28451         
28452         return this;
28453     },
28454
28455     /**
28456      * Set values for fields in this form in bulk.
28457      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28458      * @return {BasicForm} this
28459      */
28460     setValues : function(values){
28461         if(values instanceof Array){ // array of objects
28462             for(var i = 0, len = values.length; i < len; i++){
28463                 var v = values[i];
28464                 var f = this.findField(v.id);
28465                 if(f){
28466                     f.setValue(v.value);
28467                     if(this.trackResetOnLoad){
28468                         f.originalValue = f.getValue();
28469                     }
28470                 }
28471             }
28472         }else{ // object hash
28473             var field, id;
28474             for(id in values){
28475                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28476                     
28477                     if (field.setFromData && 
28478                         field.valueField && 
28479                         field.displayField &&
28480                         // combos' with local stores can 
28481                         // be queried via setValue()
28482                         // to set their value..
28483                         (field.store && !field.store.isLocal)
28484                         ) {
28485                         // it's a combo
28486                         var sd = { };
28487                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28488                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28489                         field.setFromData(sd);
28490                         
28491                     } else {
28492                         field.setValue(values[id]);
28493                     }
28494                     
28495                     
28496                     if(this.trackResetOnLoad){
28497                         field.originalValue = field.getValue();
28498                     }
28499                 }
28500             }
28501         }
28502          
28503         Roo.each(this.childForms || [], function (f) {
28504             f.setValues(values);
28505         });
28506                 
28507         return this;
28508     },
28509
28510     /**
28511      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28512      * they are returned as an array.
28513      * @param {Boolean} asString
28514      * @return {Object}
28515      */
28516     getValues : function(asString){
28517         if (this.childForms) {
28518             // copy values from the child forms
28519             Roo.each(this.childForms, function (f) {
28520                 this.setValues(f.getValues());
28521             }, this);
28522         }
28523         
28524         
28525         
28526         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28527         if(asString === true){
28528             return fs;
28529         }
28530         return Roo.urlDecode(fs);
28531     },
28532     
28533     /**
28534      * Returns the fields in this form as an object with key/value pairs. 
28535      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28536      * @return {Object}
28537      */
28538     getFieldValues : function(with_hidden)
28539     {
28540         if (this.childForms) {
28541             // copy values from the child forms
28542             // should this call getFieldValues - probably not as we do not currently copy
28543             // hidden fields when we generate..
28544             Roo.each(this.childForms, function (f) {
28545                 this.setValues(f.getValues());
28546             }, this);
28547         }
28548         
28549         var ret = {};
28550         this.items.each(function(f){
28551             if (!f.getName()) {
28552                 return;
28553             }
28554             var v = f.getValue();
28555             if (f.inputType =='radio') {
28556                 if (typeof(ret[f.getName()]) == 'undefined') {
28557                     ret[f.getName()] = ''; // empty..
28558                 }
28559                 
28560                 if (!f.el.dom.checked) {
28561                     return;
28562                     
28563                 }
28564                 v = f.el.dom.value;
28565                 
28566             }
28567             
28568             // not sure if this supported any more..
28569             if ((typeof(v) == 'object') && f.getRawValue) {
28570                 v = f.getRawValue() ; // dates..
28571             }
28572             // combo boxes where name != hiddenName...
28573             if (f.name != f.getName()) {
28574                 ret[f.name] = f.getRawValue();
28575             }
28576             ret[f.getName()] = v;
28577         });
28578         
28579         return ret;
28580     },
28581
28582     /**
28583      * Clears all invalid messages in this form.
28584      * @return {BasicForm} this
28585      */
28586     clearInvalid : function(){
28587         this.items.each(function(f){
28588            f.clearInvalid();
28589         });
28590         
28591         Roo.each(this.childForms || [], function (f) {
28592             f.clearInvalid();
28593         });
28594         
28595         
28596         return this;
28597     },
28598
28599     /**
28600      * Resets this form.
28601      * @return {BasicForm} this
28602      */
28603     reset : function(){
28604         this.items.each(function(f){
28605             f.reset();
28606         });
28607         
28608         Roo.each(this.childForms || [], function (f) {
28609             f.reset();
28610         });
28611        
28612         
28613         return this;
28614     },
28615
28616     /**
28617      * Add Roo.form components to this form.
28618      * @param {Field} field1
28619      * @param {Field} field2 (optional)
28620      * @param {Field} etc (optional)
28621      * @return {BasicForm} this
28622      */
28623     add : function(){
28624         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28625         return this;
28626     },
28627
28628
28629     /**
28630      * Removes a field from the items collection (does NOT remove its markup).
28631      * @param {Field} field
28632      * @return {BasicForm} this
28633      */
28634     remove : function(field){
28635         this.items.remove(field);
28636         return this;
28637     },
28638
28639     /**
28640      * Looks at the fields in this form, checks them for an id attribute,
28641      * and calls applyTo on the existing dom element with that id.
28642      * @return {BasicForm} this
28643      */
28644     render : function(){
28645         this.items.each(function(f){
28646             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28647                 f.applyTo(f.id);
28648             }
28649         });
28650         return this;
28651     },
28652
28653     /**
28654      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28655      * @param {Object} values
28656      * @return {BasicForm} this
28657      */
28658     applyToFields : function(o){
28659         this.items.each(function(f){
28660            Roo.apply(f, o);
28661         });
28662         return this;
28663     },
28664
28665     /**
28666      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28667      * @param {Object} values
28668      * @return {BasicForm} this
28669      */
28670     applyIfToFields : function(o){
28671         this.items.each(function(f){
28672            Roo.applyIf(f, o);
28673         });
28674         return this;
28675     }
28676 });
28677
28678 // back compat
28679 Roo.BasicForm = Roo.form.BasicForm;/*
28680  * Based on:
28681  * Ext JS Library 1.1.1
28682  * Copyright(c) 2006-2007, Ext JS, LLC.
28683  *
28684  * Originally Released Under LGPL - original licence link has changed is not relivant.
28685  *
28686  * Fork - LGPL
28687  * <script type="text/javascript">
28688  */
28689
28690 /**
28691  * @class Roo.form.Form
28692  * @extends Roo.form.BasicForm
28693  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28694  * @constructor
28695  * @param {Object} config Configuration options
28696  */
28697 Roo.form.Form = function(config){
28698     var xitems =  [];
28699     if (config.items) {
28700         xitems = config.items;
28701         delete config.items;
28702     }
28703    
28704     
28705     Roo.form.Form.superclass.constructor.call(this, null, config);
28706     this.url = this.url || this.action;
28707     if(!this.root){
28708         this.root = new Roo.form.Layout(Roo.applyIf({
28709             id: Roo.id()
28710         }, config));
28711     }
28712     this.active = this.root;
28713     /**
28714      * Array of all the buttons that have been added to this form via {@link addButton}
28715      * @type Array
28716      */
28717     this.buttons = [];
28718     this.allItems = [];
28719     this.addEvents({
28720         /**
28721          * @event clientvalidation
28722          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28723          * @param {Form} this
28724          * @param {Boolean} valid true if the form has passed client-side validation
28725          */
28726         clientvalidation: true,
28727         /**
28728          * @event rendered
28729          * Fires when the form is rendered
28730          * @param {Roo.form.Form} form
28731          */
28732         rendered : true
28733     });
28734     
28735     if (this.progressUrl) {
28736             // push a hidden field onto the list of fields..
28737             this.addxtype( {
28738                     xns: Roo.form, 
28739                     xtype : 'Hidden', 
28740                     name : 'UPLOAD_IDENTIFIER' 
28741             });
28742         }
28743         
28744     
28745     Roo.each(xitems, this.addxtype, this);
28746     
28747     
28748     
28749 };
28750
28751 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28752     /**
28753      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28754      */
28755     /**
28756      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28757      */
28758     /**
28759      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28760      */
28761     buttonAlign:'center',
28762
28763     /**
28764      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28765      */
28766     minButtonWidth:75,
28767
28768     /**
28769      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28770      * This property cascades to child containers if not set.
28771      */
28772     labelAlign:'left',
28773
28774     /**
28775      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28776      * fires a looping event with that state. This is required to bind buttons to the valid
28777      * state using the config value formBind:true on the button.
28778      */
28779     monitorValid : false,
28780
28781     /**
28782      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28783      */
28784     monitorPoll : 200,
28785     
28786     /**
28787      * @cfg {String} progressUrl - Url to return progress data 
28788      */
28789     
28790     progressUrl : false,
28791   
28792     /**
28793      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28794      * fields are added and the column is closed. If no fields are passed the column remains open
28795      * until end() is called.
28796      * @param {Object} config The config to pass to the column
28797      * @param {Field} field1 (optional)
28798      * @param {Field} field2 (optional)
28799      * @param {Field} etc (optional)
28800      * @return Column The column container object
28801      */
28802     column : function(c){
28803         var col = new Roo.form.Column(c);
28804         this.start(col);
28805         if(arguments.length > 1){ // duplicate code required because of Opera
28806             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28807             this.end();
28808         }
28809         return col;
28810     },
28811
28812     /**
28813      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28814      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28815      * until end() is called.
28816      * @param {Object} config The config to pass to the fieldset
28817      * @param {Field} field1 (optional)
28818      * @param {Field} field2 (optional)
28819      * @param {Field} etc (optional)
28820      * @return FieldSet The fieldset container object
28821      */
28822     fieldset : function(c){
28823         var fs = new Roo.form.FieldSet(c);
28824         this.start(fs);
28825         if(arguments.length > 1){ // duplicate code required because of Opera
28826             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28827             this.end();
28828         }
28829         return fs;
28830     },
28831
28832     /**
28833      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28834      * fields are added and the container is closed. If no fields are passed the container remains open
28835      * until end() is called.
28836      * @param {Object} config The config to pass to the Layout
28837      * @param {Field} field1 (optional)
28838      * @param {Field} field2 (optional)
28839      * @param {Field} etc (optional)
28840      * @return Layout The container object
28841      */
28842     container : function(c){
28843         var l = new Roo.form.Layout(c);
28844         this.start(l);
28845         if(arguments.length > 1){ // duplicate code required because of Opera
28846             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28847             this.end();
28848         }
28849         return l;
28850     },
28851
28852     /**
28853      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28854      * @param {Object} container A Roo.form.Layout or subclass of Layout
28855      * @return {Form} this
28856      */
28857     start : function(c){
28858         // cascade label info
28859         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28860         this.active.stack.push(c);
28861         c.ownerCt = this.active;
28862         this.active = c;
28863         return this;
28864     },
28865
28866     /**
28867      * Closes the current open container
28868      * @return {Form} this
28869      */
28870     end : function(){
28871         if(this.active == this.root){
28872             return this;
28873         }
28874         this.active = this.active.ownerCt;
28875         return this;
28876     },
28877
28878     /**
28879      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28880      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28881      * as the label of the field.
28882      * @param {Field} field1
28883      * @param {Field} field2 (optional)
28884      * @param {Field} etc. (optional)
28885      * @return {Form} this
28886      */
28887     add : function(){
28888         this.active.stack.push.apply(this.active.stack, arguments);
28889         this.allItems.push.apply(this.allItems,arguments);
28890         var r = [];
28891         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28892             if(a[i].isFormField){
28893                 r.push(a[i]);
28894             }
28895         }
28896         if(r.length > 0){
28897             Roo.form.Form.superclass.add.apply(this, r);
28898         }
28899         return this;
28900     },
28901     
28902
28903     
28904     
28905     
28906      /**
28907      * Find any element that has been added to a form, using it's ID or name
28908      * This can include framesets, columns etc. along with regular fields..
28909      * @param {String} id - id or name to find.
28910      
28911      * @return {Element} e - or false if nothing found.
28912      */
28913     findbyId : function(id)
28914     {
28915         var ret = false;
28916         if (!id) {
28917             return ret;
28918         }
28919         Roo.each(this.allItems, function(f){
28920             if (f.id == id || f.name == id ){
28921                 ret = f;
28922                 return false;
28923             }
28924         });
28925         return ret;
28926     },
28927
28928     
28929     
28930     /**
28931      * Render this form into the passed container. This should only be called once!
28932      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28933      * @return {Form} this
28934      */
28935     render : function(ct)
28936     {
28937         
28938         
28939         
28940         ct = Roo.get(ct);
28941         var o = this.autoCreate || {
28942             tag: 'form',
28943             method : this.method || 'POST',
28944             id : this.id || Roo.id()
28945         };
28946         this.initEl(ct.createChild(o));
28947
28948         this.root.render(this.el);
28949         
28950        
28951              
28952         this.items.each(function(f){
28953             f.render('x-form-el-'+f.id);
28954         });
28955
28956         if(this.buttons.length > 0){
28957             // tables are required to maintain order and for correct IE layout
28958             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28959                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28960                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28961             }}, null, true);
28962             var tr = tb.getElementsByTagName('tr')[0];
28963             for(var i = 0, len = this.buttons.length; i < len; i++) {
28964                 var b = this.buttons[i];
28965                 var td = document.createElement('td');
28966                 td.className = 'x-form-btn-td';
28967                 b.render(tr.appendChild(td));
28968             }
28969         }
28970         if(this.monitorValid){ // initialize after render
28971             this.startMonitoring();
28972         }
28973         this.fireEvent('rendered', this);
28974         return this;
28975     },
28976
28977     /**
28978      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28979      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28980      * object or a valid Roo.DomHelper element config
28981      * @param {Function} handler The function called when the button is clicked
28982      * @param {Object} scope (optional) The scope of the handler function
28983      * @return {Roo.Button}
28984      */
28985     addButton : function(config, handler, scope){
28986         var bc = {
28987             handler: handler,
28988             scope: scope,
28989             minWidth: this.minButtonWidth,
28990             hideParent:true
28991         };
28992         if(typeof config == "string"){
28993             bc.text = config;
28994         }else{
28995             Roo.apply(bc, config);
28996         }
28997         var btn = new Roo.Button(null, bc);
28998         this.buttons.push(btn);
28999         return btn;
29000     },
29001
29002      /**
29003      * Adds a series of form elements (using the xtype property as the factory method.
29004      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29005      * @param {Object} config 
29006      */
29007     
29008     addxtype : function()
29009     {
29010         var ar = Array.prototype.slice.call(arguments, 0);
29011         var ret = false;
29012         for(var i = 0; i < ar.length; i++) {
29013             if (!ar[i]) {
29014                 continue; // skip -- if this happends something invalid got sent, we 
29015                 // should ignore it, as basically that interface element will not show up
29016                 // and that should be pretty obvious!!
29017             }
29018             
29019             if (Roo.form[ar[i].xtype]) {
29020                 ar[i].form = this;
29021                 var fe = Roo.factory(ar[i], Roo.form);
29022                 if (!ret) {
29023                     ret = fe;
29024                 }
29025                 fe.form = this;
29026                 if (fe.store) {
29027                     fe.store.form = this;
29028                 }
29029                 if (fe.isLayout) {  
29030                          
29031                     this.start(fe);
29032                     this.allItems.push(fe);
29033                     if (fe.items && fe.addxtype) {
29034                         fe.addxtype.apply(fe, fe.items);
29035                         delete fe.items;
29036                     }
29037                      this.end();
29038                     continue;
29039                 }
29040                 
29041                 
29042                  
29043                 this.add(fe);
29044               //  console.log('adding ' + ar[i].xtype);
29045             }
29046             if (ar[i].xtype == 'Button') {  
29047                 //console.log('adding button');
29048                 //console.log(ar[i]);
29049                 this.addButton(ar[i]);
29050                 this.allItems.push(fe);
29051                 continue;
29052             }
29053             
29054             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29055                 alert('end is not supported on xtype any more, use items');
29056             //    this.end();
29057             //    //console.log('adding end');
29058             }
29059             
29060         }
29061         return ret;
29062     },
29063     
29064     /**
29065      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29066      * option "monitorValid"
29067      */
29068     startMonitoring : function(){
29069         if(!this.bound){
29070             this.bound = true;
29071             Roo.TaskMgr.start({
29072                 run : this.bindHandler,
29073                 interval : this.monitorPoll || 200,
29074                 scope: this
29075             });
29076         }
29077     },
29078
29079     /**
29080      * Stops monitoring of the valid state of this form
29081      */
29082     stopMonitoring : function(){
29083         this.bound = false;
29084     },
29085
29086     // private
29087     bindHandler : function(){
29088         if(!this.bound){
29089             return false; // stops binding
29090         }
29091         var valid = true;
29092         this.items.each(function(f){
29093             if(!f.isValid(true)){
29094                 valid = false;
29095                 return false;
29096             }
29097         });
29098         for(var i = 0, len = this.buttons.length; i < len; i++){
29099             var btn = this.buttons[i];
29100             if(btn.formBind === true && btn.disabled === valid){
29101                 btn.setDisabled(!valid);
29102             }
29103         }
29104         this.fireEvent('clientvalidation', this, valid);
29105     }
29106     
29107     
29108     
29109     
29110     
29111     
29112     
29113     
29114 });
29115
29116
29117 // back compat
29118 Roo.Form = Roo.form.Form;
29119 /*
29120  * Based on:
29121  * Ext JS Library 1.1.1
29122  * Copyright(c) 2006-2007, Ext JS, LLC.
29123  *
29124  * Originally Released Under LGPL - original licence link has changed is not relivant.
29125  *
29126  * Fork - LGPL
29127  * <script type="text/javascript">
29128  */
29129  
29130  /**
29131  * @class Roo.form.Action
29132  * Internal Class used to handle form actions
29133  * @constructor
29134  * @param {Roo.form.BasicForm} el The form element or its id
29135  * @param {Object} config Configuration options
29136  */
29137  
29138  
29139 // define the action interface
29140 Roo.form.Action = function(form, options){
29141     this.form = form;
29142     this.options = options || {};
29143 };
29144 /**
29145  * Client Validation Failed
29146  * @const 
29147  */
29148 Roo.form.Action.CLIENT_INVALID = 'client';
29149 /**
29150  * Server Validation Failed
29151  * @const 
29152  */
29153  Roo.form.Action.SERVER_INVALID = 'server';
29154  /**
29155  * Connect to Server Failed
29156  * @const 
29157  */
29158 Roo.form.Action.CONNECT_FAILURE = 'connect';
29159 /**
29160  * Reading Data from Server Failed
29161  * @const 
29162  */
29163 Roo.form.Action.LOAD_FAILURE = 'load';
29164
29165 Roo.form.Action.prototype = {
29166     type : 'default',
29167     failureType : undefined,
29168     response : undefined,
29169     result : undefined,
29170
29171     // interface method
29172     run : function(options){
29173
29174     },
29175
29176     // interface method
29177     success : function(response){
29178
29179     },
29180
29181     // interface method
29182     handleResponse : function(response){
29183
29184     },
29185
29186     // default connection failure
29187     failure : function(response){
29188         
29189         this.response = response;
29190         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29191         this.form.afterAction(this, false);
29192     },
29193
29194     processResponse : function(response){
29195         this.response = response;
29196         if(!response.responseText){
29197             return true;
29198         }
29199         this.result = this.handleResponse(response);
29200         return this.result;
29201     },
29202
29203     // utility functions used internally
29204     getUrl : function(appendParams){
29205         var url = this.options.url || this.form.url || this.form.el.dom.action;
29206         if(appendParams){
29207             var p = this.getParams();
29208             if(p){
29209                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29210             }
29211         }
29212         return url;
29213     },
29214
29215     getMethod : function(){
29216         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29217     },
29218
29219     getParams : function(){
29220         var bp = this.form.baseParams;
29221         var p = this.options.params;
29222         if(p){
29223             if(typeof p == "object"){
29224                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29225             }else if(typeof p == 'string' && bp){
29226                 p += '&' + Roo.urlEncode(bp);
29227             }
29228         }else if(bp){
29229             p = Roo.urlEncode(bp);
29230         }
29231         return p;
29232     },
29233
29234     createCallback : function(){
29235         return {
29236             success: this.success,
29237             failure: this.failure,
29238             scope: this,
29239             timeout: (this.form.timeout*1000),
29240             upload: this.form.fileUpload ? this.success : undefined
29241         };
29242     }
29243 };
29244
29245 Roo.form.Action.Submit = function(form, options){
29246     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29247 };
29248
29249 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29250     type : 'submit',
29251
29252     haveProgress : false,
29253     uploadComplete : false,
29254     
29255     // uploadProgress indicator.
29256     uploadProgress : function()
29257     {
29258         if (!this.form.progressUrl) {
29259             return;
29260         }
29261         
29262         if (!this.haveProgress) {
29263             Roo.MessageBox.progress("Uploading", "Uploading");
29264         }
29265         if (this.uploadComplete) {
29266            Roo.MessageBox.hide();
29267            return;
29268         }
29269         
29270         this.haveProgress = true;
29271    
29272         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29273         
29274         var c = new Roo.data.Connection();
29275         c.request({
29276             url : this.form.progressUrl,
29277             params: {
29278                 id : uid
29279             },
29280             method: 'GET',
29281             success : function(req){
29282                //console.log(data);
29283                 var rdata = false;
29284                 var edata;
29285                 try  {
29286                    rdata = Roo.decode(req.responseText)
29287                 } catch (e) {
29288                     Roo.log("Invalid data from server..");
29289                     Roo.log(edata);
29290                     return;
29291                 }
29292                 if (!rdata || !rdata.success) {
29293                     Roo.log(rdata);
29294                     Roo.MessageBox.alert(Roo.encode(rdata));
29295                     return;
29296                 }
29297                 var data = rdata.data;
29298                 
29299                 if (this.uploadComplete) {
29300                    Roo.MessageBox.hide();
29301                    return;
29302                 }
29303                    
29304                 if (data){
29305                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29306                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29307                     );
29308                 }
29309                 this.uploadProgress.defer(2000,this);
29310             },
29311        
29312             failure: function(data) {
29313                 Roo.log('progress url failed ');
29314                 Roo.log(data);
29315             },
29316             scope : this
29317         });
29318            
29319     },
29320     
29321     
29322     run : function()
29323     {
29324         // run get Values on the form, so it syncs any secondary forms.
29325         this.form.getValues();
29326         
29327         var o = this.options;
29328         var method = this.getMethod();
29329         var isPost = method == 'POST';
29330         if(o.clientValidation === false || this.form.isValid()){
29331             
29332             if (this.form.progressUrl) {
29333                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29334                     (new Date() * 1) + '' + Math.random());
29335                     
29336             } 
29337             
29338             
29339             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29340                 form:this.form.el.dom,
29341                 url:this.getUrl(!isPost),
29342                 method: method,
29343                 params:isPost ? this.getParams() : null,
29344                 isUpload: this.form.fileUpload
29345             }));
29346             
29347             this.uploadProgress();
29348
29349         }else if (o.clientValidation !== false){ // client validation failed
29350             this.failureType = Roo.form.Action.CLIENT_INVALID;
29351             this.form.afterAction(this, false);
29352         }
29353     },
29354
29355     success : function(response)
29356     {
29357         this.uploadComplete= true;
29358         if (this.haveProgress) {
29359             Roo.MessageBox.hide();
29360         }
29361         
29362         
29363         var result = this.processResponse(response);
29364         if(result === true || result.success){
29365             this.form.afterAction(this, true);
29366             return;
29367         }
29368         if(result.errors){
29369             this.form.markInvalid(result.errors);
29370             this.failureType = Roo.form.Action.SERVER_INVALID;
29371         }
29372         this.form.afterAction(this, false);
29373     },
29374     failure : function(response)
29375     {
29376         this.uploadComplete= true;
29377         if (this.haveProgress) {
29378             Roo.MessageBox.hide();
29379         }
29380         
29381         this.response = response;
29382         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29383         this.form.afterAction(this, false);
29384     },
29385     
29386     handleResponse : function(response){
29387         if(this.form.errorReader){
29388             var rs = this.form.errorReader.read(response);
29389             var errors = [];
29390             if(rs.records){
29391                 for(var i = 0, len = rs.records.length; i < len; i++) {
29392                     var r = rs.records[i];
29393                     errors[i] = r.data;
29394                 }
29395             }
29396             if(errors.length < 1){
29397                 errors = null;
29398             }
29399             return {
29400                 success : rs.success,
29401                 errors : errors
29402             };
29403         }
29404         var ret = false;
29405         try {
29406             ret = Roo.decode(response.responseText);
29407         } catch (e) {
29408             ret = {
29409                 success: false,
29410                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29411                 errors : []
29412             };
29413         }
29414         return ret;
29415         
29416     }
29417 });
29418
29419
29420 Roo.form.Action.Load = function(form, options){
29421     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29422     this.reader = this.form.reader;
29423 };
29424
29425 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29426     type : 'load',
29427
29428     run : function(){
29429         
29430         Roo.Ajax.request(Roo.apply(
29431                 this.createCallback(), {
29432                     method:this.getMethod(),
29433                     url:this.getUrl(false),
29434                     params:this.getParams()
29435         }));
29436     },
29437
29438     success : function(response){
29439         
29440         var result = this.processResponse(response);
29441         if(result === true || !result.success || !result.data){
29442             this.failureType = Roo.form.Action.LOAD_FAILURE;
29443             this.form.afterAction(this, false);
29444             return;
29445         }
29446         this.form.clearInvalid();
29447         this.form.setValues(result.data);
29448         this.form.afterAction(this, true);
29449     },
29450
29451     handleResponse : function(response){
29452         if(this.form.reader){
29453             var rs = this.form.reader.read(response);
29454             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29455             return {
29456                 success : rs.success,
29457                 data : data
29458             };
29459         }
29460         return Roo.decode(response.responseText);
29461     }
29462 });
29463
29464 Roo.form.Action.ACTION_TYPES = {
29465     'load' : Roo.form.Action.Load,
29466     'submit' : Roo.form.Action.Submit
29467 };/*
29468  * Based on:
29469  * Ext JS Library 1.1.1
29470  * Copyright(c) 2006-2007, Ext JS, LLC.
29471  *
29472  * Originally Released Under LGPL - original licence link has changed is not relivant.
29473  *
29474  * Fork - LGPL
29475  * <script type="text/javascript">
29476  */
29477  
29478 /**
29479  * @class Roo.form.Layout
29480  * @extends Roo.Component
29481  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29482  * @constructor
29483  * @param {Object} config Configuration options
29484  */
29485 Roo.form.Layout = function(config){
29486     var xitems = [];
29487     if (config.items) {
29488         xitems = config.items;
29489         delete config.items;
29490     }
29491     Roo.form.Layout.superclass.constructor.call(this, config);
29492     this.stack = [];
29493     Roo.each(xitems, this.addxtype, this);
29494      
29495 };
29496
29497 Roo.extend(Roo.form.Layout, Roo.Component, {
29498     /**
29499      * @cfg {String/Object} autoCreate
29500      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29501      */
29502     /**
29503      * @cfg {String/Object/Function} style
29504      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29505      * a function which returns such a specification.
29506      */
29507     /**
29508      * @cfg {String} labelAlign
29509      * Valid values are "left," "top" and "right" (defaults to "left")
29510      */
29511     /**
29512      * @cfg {Number} labelWidth
29513      * Fixed width in pixels of all field labels (defaults to undefined)
29514      */
29515     /**
29516      * @cfg {Boolean} clear
29517      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29518      */
29519     clear : true,
29520     /**
29521      * @cfg {String} labelSeparator
29522      * The separator to use after field labels (defaults to ':')
29523      */
29524     labelSeparator : ':',
29525     /**
29526      * @cfg {Boolean} hideLabels
29527      * True to suppress the display of field labels in this layout (defaults to false)
29528      */
29529     hideLabels : false,
29530
29531     // private
29532     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29533     
29534     isLayout : true,
29535     
29536     // private
29537     onRender : function(ct, position){
29538         if(this.el){ // from markup
29539             this.el = Roo.get(this.el);
29540         }else {  // generate
29541             var cfg = this.getAutoCreate();
29542             this.el = ct.createChild(cfg, position);
29543         }
29544         if(this.style){
29545             this.el.applyStyles(this.style);
29546         }
29547         if(this.labelAlign){
29548             this.el.addClass('x-form-label-'+this.labelAlign);
29549         }
29550         if(this.hideLabels){
29551             this.labelStyle = "display:none";
29552             this.elementStyle = "padding-left:0;";
29553         }else{
29554             if(typeof this.labelWidth == 'number'){
29555                 this.labelStyle = "width:"+this.labelWidth+"px;";
29556                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29557             }
29558             if(this.labelAlign == 'top'){
29559                 this.labelStyle = "width:auto;";
29560                 this.elementStyle = "padding-left:0;";
29561             }
29562         }
29563         var stack = this.stack;
29564         var slen = stack.length;
29565         if(slen > 0){
29566             if(!this.fieldTpl){
29567                 var t = new Roo.Template(
29568                     '<div class="x-form-item {5}">',
29569                         '<label for="{0}" style="{2}">{1}{4}</label>',
29570                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29571                         '</div>',
29572                     '</div><div class="x-form-clear-left"></div>'
29573                 );
29574                 t.disableFormats = true;
29575                 t.compile();
29576                 Roo.form.Layout.prototype.fieldTpl = t;
29577             }
29578             for(var i = 0; i < slen; i++) {
29579                 if(stack[i].isFormField){
29580                     this.renderField(stack[i]);
29581                 }else{
29582                     this.renderComponent(stack[i]);
29583                 }
29584             }
29585         }
29586         if(this.clear){
29587             this.el.createChild({cls:'x-form-clear'});
29588         }
29589     },
29590
29591     // private
29592     renderField : function(f){
29593         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29594                f.id, //0
29595                f.fieldLabel, //1
29596                f.labelStyle||this.labelStyle||'', //2
29597                this.elementStyle||'', //3
29598                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29599                f.itemCls||this.itemCls||''  //5
29600        ], true).getPrevSibling());
29601     },
29602
29603     // private
29604     renderComponent : function(c){
29605         c.render(c.isLayout ? this.el : this.el.createChild());    
29606     },
29607     /**
29608      * Adds a object form elements (using the xtype property as the factory method.)
29609      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29610      * @param {Object} config 
29611      */
29612     addxtype : function(o)
29613     {
29614         // create the lement.
29615         o.form = this.form;
29616         var fe = Roo.factory(o, Roo.form);
29617         this.form.allItems.push(fe);
29618         this.stack.push(fe);
29619         
29620         if (fe.isFormField) {
29621             this.form.items.add(fe);
29622         }
29623          
29624         return fe;
29625     }
29626 });
29627
29628 /**
29629  * @class Roo.form.Column
29630  * @extends Roo.form.Layout
29631  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29632  * @constructor
29633  * @param {Object} config Configuration options
29634  */
29635 Roo.form.Column = function(config){
29636     Roo.form.Column.superclass.constructor.call(this, config);
29637 };
29638
29639 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29640     /**
29641      * @cfg {Number/String} width
29642      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29643      */
29644     /**
29645      * @cfg {String/Object} autoCreate
29646      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29647      */
29648
29649     // private
29650     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29651
29652     // private
29653     onRender : function(ct, position){
29654         Roo.form.Column.superclass.onRender.call(this, ct, position);
29655         if(this.width){
29656             this.el.setWidth(this.width);
29657         }
29658     }
29659 });
29660
29661
29662 /**
29663  * @class Roo.form.Row
29664  * @extends Roo.form.Layout
29665  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29666  * @constructor
29667  * @param {Object} config Configuration options
29668  */
29669
29670  
29671 Roo.form.Row = function(config){
29672     Roo.form.Row.superclass.constructor.call(this, config);
29673 };
29674  
29675 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29676       /**
29677      * @cfg {Number/String} width
29678      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29679      */
29680     /**
29681      * @cfg {Number/String} height
29682      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29683      */
29684     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29685     
29686     padWidth : 20,
29687     // private
29688     onRender : function(ct, position){
29689         //console.log('row render');
29690         if(!this.rowTpl){
29691             var t = new Roo.Template(
29692                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29693                     '<label for="{0}" style="{2}">{1}{4}</label>',
29694                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29695                     '</div>',
29696                 '</div>'
29697             );
29698             t.disableFormats = true;
29699             t.compile();
29700             Roo.form.Layout.prototype.rowTpl = t;
29701         }
29702         this.fieldTpl = this.rowTpl;
29703         
29704         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29705         var labelWidth = 100;
29706         
29707         if ((this.labelAlign != 'top')) {
29708             if (typeof this.labelWidth == 'number') {
29709                 labelWidth = this.labelWidth
29710             }
29711             this.padWidth =  20 + labelWidth;
29712             
29713         }
29714         
29715         Roo.form.Column.superclass.onRender.call(this, ct, position);
29716         if(this.width){
29717             this.el.setWidth(this.width);
29718         }
29719         if(this.height){
29720             this.el.setHeight(this.height);
29721         }
29722     },
29723     
29724     // private
29725     renderField : function(f){
29726         f.fieldEl = this.fieldTpl.append(this.el, [
29727                f.id, f.fieldLabel,
29728                f.labelStyle||this.labelStyle||'',
29729                this.elementStyle||'',
29730                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29731                f.itemCls||this.itemCls||'',
29732                f.width ? f.width + this.padWidth : 160 + this.padWidth
29733        ],true);
29734     }
29735 });
29736  
29737
29738 /**
29739  * @class Roo.form.FieldSet
29740  * @extends Roo.form.Layout
29741  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29742  * @constructor
29743  * @param {Object} config Configuration options
29744  */
29745 Roo.form.FieldSet = function(config){
29746     Roo.form.FieldSet.superclass.constructor.call(this, config);
29747 };
29748
29749 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29750     /**
29751      * @cfg {String} legend
29752      * The text to display as the legend for the FieldSet (defaults to '')
29753      */
29754     /**
29755      * @cfg {String/Object} autoCreate
29756      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29757      */
29758
29759     // private
29760     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29761
29762     // private
29763     onRender : function(ct, position){
29764         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29765         if(this.legend){
29766             this.setLegend(this.legend);
29767         }
29768     },
29769
29770     // private
29771     setLegend : function(text){
29772         if(this.rendered){
29773             this.el.child('legend').update(text);
29774         }
29775     }
29776 });/*
29777  * Based on:
29778  * Ext JS Library 1.1.1
29779  * Copyright(c) 2006-2007, Ext JS, LLC.
29780  *
29781  * Originally Released Under LGPL - original licence link has changed is not relivant.
29782  *
29783  * Fork - LGPL
29784  * <script type="text/javascript">
29785  */
29786 /**
29787  * @class Roo.form.VTypes
29788  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29789  * @singleton
29790  */
29791 Roo.form.VTypes = function(){
29792     // closure these in so they are only created once.
29793     var alpha = /^[a-zA-Z_]+$/;
29794     var alphanum = /^[a-zA-Z0-9_]+$/;
29795     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29796     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29797
29798     // All these messages and functions are configurable
29799     return {
29800         /**
29801          * The function used to validate email addresses
29802          * @param {String} value The email address
29803          */
29804         'email' : function(v){
29805             return email.test(v);
29806         },
29807         /**
29808          * The error text to display when the email validation function returns false
29809          * @type String
29810          */
29811         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29812         /**
29813          * The keystroke filter mask to be applied on email input
29814          * @type RegExp
29815          */
29816         'emailMask' : /[a-z0-9_\.\-@]/i,
29817
29818         /**
29819          * The function used to validate URLs
29820          * @param {String} value The URL
29821          */
29822         'url' : function(v){
29823             return url.test(v);
29824         },
29825         /**
29826          * The error text to display when the url validation function returns false
29827          * @type String
29828          */
29829         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29830         
29831         /**
29832          * The function used to validate alpha values
29833          * @param {String} value The value
29834          */
29835         'alpha' : function(v){
29836             return alpha.test(v);
29837         },
29838         /**
29839          * The error text to display when the alpha validation function returns false
29840          * @type String
29841          */
29842         'alphaText' : 'This field should only contain letters and _',
29843         /**
29844          * The keystroke filter mask to be applied on alpha input
29845          * @type RegExp
29846          */
29847         'alphaMask' : /[a-z_]/i,
29848
29849         /**
29850          * The function used to validate alphanumeric values
29851          * @param {String} value The value
29852          */
29853         'alphanum' : function(v){
29854             return alphanum.test(v);
29855         },
29856         /**
29857          * The error text to display when the alphanumeric validation function returns false
29858          * @type String
29859          */
29860         'alphanumText' : 'This field should only contain letters, numbers and _',
29861         /**
29862          * The keystroke filter mask to be applied on alphanumeric input
29863          * @type RegExp
29864          */
29865         'alphanumMask' : /[a-z0-9_]/i
29866     };
29867 }();//<script type="text/javascript">
29868
29869 /**
29870  * @class Roo.form.FCKeditor
29871  * @extends Roo.form.TextArea
29872  * Wrapper around the FCKEditor http://www.fckeditor.net
29873  * @constructor
29874  * Creates a new FCKeditor
29875  * @param {Object} config Configuration options
29876  */
29877 Roo.form.FCKeditor = function(config){
29878     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29879     this.addEvents({
29880          /**
29881          * @event editorinit
29882          * Fired when the editor is initialized - you can add extra handlers here..
29883          * @param {FCKeditor} this
29884          * @param {Object} the FCK object.
29885          */
29886         editorinit : true
29887     });
29888     
29889     
29890 };
29891 Roo.form.FCKeditor.editors = { };
29892 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29893 {
29894     //defaultAutoCreate : {
29895     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29896     //},
29897     // private
29898     /**
29899      * @cfg {Object} fck options - see fck manual for details.
29900      */
29901     fckconfig : false,
29902     
29903     /**
29904      * @cfg {Object} fck toolbar set (Basic or Default)
29905      */
29906     toolbarSet : 'Basic',
29907     /**
29908      * @cfg {Object} fck BasePath
29909      */ 
29910     basePath : '/fckeditor/',
29911     
29912     
29913     frame : false,
29914     
29915     value : '',
29916     
29917    
29918     onRender : function(ct, position)
29919     {
29920         if(!this.el){
29921             this.defaultAutoCreate = {
29922                 tag: "textarea",
29923                 style:"width:300px;height:60px;",
29924                 autocomplete: "off"
29925             };
29926         }
29927         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29928         /*
29929         if(this.grow){
29930             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29931             if(this.preventScrollbars){
29932                 this.el.setStyle("overflow", "hidden");
29933             }
29934             this.el.setHeight(this.growMin);
29935         }
29936         */
29937         //console.log('onrender' + this.getId() );
29938         Roo.form.FCKeditor.editors[this.getId()] = this;
29939          
29940
29941         this.replaceTextarea() ;
29942         
29943     },
29944     
29945     getEditor : function() {
29946         return this.fckEditor;
29947     },
29948     /**
29949      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29950      * @param {Mixed} value The value to set
29951      */
29952     
29953     
29954     setValue : function(value)
29955     {
29956         //console.log('setValue: ' + value);
29957         
29958         if(typeof(value) == 'undefined') { // not sure why this is happending...
29959             return;
29960         }
29961         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29962         
29963         //if(!this.el || !this.getEditor()) {
29964         //    this.value = value;
29965             //this.setValue.defer(100,this,[value]);    
29966         //    return;
29967         //} 
29968         
29969         if(!this.getEditor()) {
29970             return;
29971         }
29972         
29973         this.getEditor().SetData(value);
29974         
29975         //
29976
29977     },
29978
29979     /**
29980      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29981      * @return {Mixed} value The field value
29982      */
29983     getValue : function()
29984     {
29985         
29986         if (this.frame && this.frame.dom.style.display == 'none') {
29987             return Roo.form.FCKeditor.superclass.getValue.call(this);
29988         }
29989         
29990         if(!this.el || !this.getEditor()) {
29991            
29992            // this.getValue.defer(100,this); 
29993             return this.value;
29994         }
29995        
29996         
29997         var value=this.getEditor().GetData();
29998         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29999         return Roo.form.FCKeditor.superclass.getValue.call(this);
30000         
30001
30002     },
30003
30004     /**
30005      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30006      * @return {Mixed} value The field value
30007      */
30008     getRawValue : function()
30009     {
30010         if (this.frame && this.frame.dom.style.display == 'none') {
30011             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30012         }
30013         
30014         if(!this.el || !this.getEditor()) {
30015             //this.getRawValue.defer(100,this); 
30016             return this.value;
30017             return;
30018         }
30019         
30020         
30021         
30022         var value=this.getEditor().GetData();
30023         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30024         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30025          
30026     },
30027     
30028     setSize : function(w,h) {
30029         
30030         
30031         
30032         //if (this.frame && this.frame.dom.style.display == 'none') {
30033         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30034         //    return;
30035         //}
30036         //if(!this.el || !this.getEditor()) {
30037         //    this.setSize.defer(100,this, [w,h]); 
30038         //    return;
30039         //}
30040         
30041         
30042         
30043         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30044         
30045         this.frame.dom.setAttribute('width', w);
30046         this.frame.dom.setAttribute('height', h);
30047         this.frame.setSize(w,h);
30048         
30049     },
30050     
30051     toggleSourceEdit : function(value) {
30052         
30053       
30054          
30055         this.el.dom.style.display = value ? '' : 'none';
30056         this.frame.dom.style.display = value ?  'none' : '';
30057         
30058     },
30059     
30060     
30061     focus: function(tag)
30062     {
30063         if (this.frame.dom.style.display == 'none') {
30064             return Roo.form.FCKeditor.superclass.focus.call(this);
30065         }
30066         if(!this.el || !this.getEditor()) {
30067             this.focus.defer(100,this, [tag]); 
30068             return;
30069         }
30070         
30071         
30072         
30073         
30074         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30075         this.getEditor().Focus();
30076         if (tgs.length) {
30077             if (!this.getEditor().Selection.GetSelection()) {
30078                 this.focus.defer(100,this, [tag]); 
30079                 return;
30080             }
30081             
30082             
30083             var r = this.getEditor().EditorDocument.createRange();
30084             r.setStart(tgs[0],0);
30085             r.setEnd(tgs[0],0);
30086             this.getEditor().Selection.GetSelection().removeAllRanges();
30087             this.getEditor().Selection.GetSelection().addRange(r);
30088             this.getEditor().Focus();
30089         }
30090         
30091     },
30092     
30093     
30094     
30095     replaceTextarea : function()
30096     {
30097         if ( document.getElementById( this.getId() + '___Frame' ) )
30098             return ;
30099         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30100         //{
30101             // We must check the elements firstly using the Id and then the name.
30102         var oTextarea = document.getElementById( this.getId() );
30103         
30104         var colElementsByName = document.getElementsByName( this.getId() ) ;
30105          
30106         oTextarea.style.display = 'none' ;
30107
30108         if ( oTextarea.tabIndex ) {            
30109             this.TabIndex = oTextarea.tabIndex ;
30110         }
30111         
30112         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30113         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30114         this.frame = Roo.get(this.getId() + '___Frame')
30115     },
30116     
30117     _getConfigHtml : function()
30118     {
30119         var sConfig = '' ;
30120
30121         for ( var o in this.fckconfig ) {
30122             sConfig += sConfig.length > 0  ? '&amp;' : '';
30123             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30124         }
30125
30126         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30127     },
30128     
30129     
30130     _getIFrameHtml : function()
30131     {
30132         var sFile = 'fckeditor.html' ;
30133         /* no idea what this is about..
30134         try
30135         {
30136             if ( (/fcksource=true/i).test( window.top.location.search ) )
30137                 sFile = 'fckeditor.original.html' ;
30138         }
30139         catch (e) { 
30140         */
30141
30142         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30143         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30144         
30145         
30146         var html = '<iframe id="' + this.getId() +
30147             '___Frame" src="' + sLink +
30148             '" width="' + this.width +
30149             '" height="' + this.height + '"' +
30150             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30151             ' frameborder="0" scrolling="no"></iframe>' ;
30152
30153         return html ;
30154     },
30155     
30156     _insertHtmlBefore : function( html, element )
30157     {
30158         if ( element.insertAdjacentHTML )       {
30159             // IE
30160             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30161         } else { // Gecko
30162             var oRange = document.createRange() ;
30163             oRange.setStartBefore( element ) ;
30164             var oFragment = oRange.createContextualFragment( html );
30165             element.parentNode.insertBefore( oFragment, element ) ;
30166         }
30167     }
30168     
30169     
30170   
30171     
30172     
30173     
30174     
30175
30176 });
30177
30178 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30179
30180 function FCKeditor_OnComplete(editorInstance){
30181     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30182     f.fckEditor = editorInstance;
30183     //console.log("loaded");
30184     f.fireEvent('editorinit', f, editorInstance);
30185
30186   
30187
30188  
30189
30190
30191
30192
30193
30194
30195
30196
30197
30198
30199
30200
30201
30202
30203
30204 //<script type="text/javascript">
30205 /**
30206  * @class Roo.form.GridField
30207  * @extends Roo.form.Field
30208  * Embed a grid (or editable grid into a form)
30209  * STATUS ALPHA
30210  * 
30211  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30212  * it needs 
30213  * xgrid.store = Roo.data.Store
30214  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30215  * xgrid.store.reader = Roo.data.JsonReader 
30216  * 
30217  * 
30218  * @constructor
30219  * Creates a new GridField
30220  * @param {Object} config Configuration options
30221  */
30222 Roo.form.GridField = function(config){
30223     Roo.form.GridField.superclass.constructor.call(this, config);
30224      
30225 };
30226
30227 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30228     /**
30229      * @cfg {Number} width  - used to restrict width of grid..
30230      */
30231     width : 100,
30232     /**
30233      * @cfg {Number} height - used to restrict height of grid..
30234      */
30235     height : 50,
30236      /**
30237      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30238          * 
30239          *}
30240      */
30241     xgrid : false, 
30242     /**
30243      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30244      * {tag: "input", type: "checkbox", autocomplete: "off"})
30245      */
30246    // defaultAutoCreate : { tag: 'div' },
30247     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30248     /**
30249      * @cfg {String} addTitle Text to include for adding a title.
30250      */
30251     addTitle : false,
30252     //
30253     onResize : function(){
30254         Roo.form.Field.superclass.onResize.apply(this, arguments);
30255     },
30256
30257     initEvents : function(){
30258         // Roo.form.Checkbox.superclass.initEvents.call(this);
30259         // has no events...
30260        
30261     },
30262
30263
30264     getResizeEl : function(){
30265         return this.wrap;
30266     },
30267
30268     getPositionEl : function(){
30269         return this.wrap;
30270     },
30271
30272     // private
30273     onRender : function(ct, position){
30274         
30275         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30276         var style = this.style;
30277         delete this.style;
30278         
30279         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30280         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30281         this.viewEl = this.wrap.createChild({ tag: 'div' });
30282         if (style) {
30283             this.viewEl.applyStyles(style);
30284         }
30285         if (this.width) {
30286             this.viewEl.setWidth(this.width);
30287         }
30288         if (this.height) {
30289             this.viewEl.setHeight(this.height);
30290         }
30291         //if(this.inputValue !== undefined){
30292         //this.setValue(this.value);
30293         
30294         
30295         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30296         
30297         
30298         this.grid.render();
30299         this.grid.getDataSource().on('remove', this.refreshValue, this);
30300         this.grid.getDataSource().on('update', this.refreshValue, this);
30301         this.grid.on('afteredit', this.refreshValue, this);
30302  
30303     },
30304      
30305     
30306     /**
30307      * Sets the value of the item. 
30308      * @param {String} either an object  or a string..
30309      */
30310     setValue : function(v){
30311         //this.value = v;
30312         v = v || []; // empty set..
30313         // this does not seem smart - it really only affects memoryproxy grids..
30314         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30315             var ds = this.grid.getDataSource();
30316             // assumes a json reader..
30317             var data = {}
30318             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30319             ds.loadData( data);
30320         }
30321         // clear selection so it does not get stale.
30322         if (this.grid.sm) { 
30323             this.grid.sm.clearSelections();
30324         }
30325         
30326         Roo.form.GridField.superclass.setValue.call(this, v);
30327         this.refreshValue();
30328         // should load data in the grid really....
30329     },
30330     
30331     // private
30332     refreshValue: function() {
30333          var val = [];
30334         this.grid.getDataSource().each(function(r) {
30335             val.push(r.data);
30336         });
30337         this.el.dom.value = Roo.encode(val);
30338     }
30339     
30340      
30341     
30342     
30343 });/*
30344  * Based on:
30345  * Ext JS Library 1.1.1
30346  * Copyright(c) 2006-2007, Ext JS, LLC.
30347  *
30348  * Originally Released Under LGPL - original licence link has changed is not relivant.
30349  *
30350  * Fork - LGPL
30351  * <script type="text/javascript">
30352  */
30353 /**
30354  * @class Roo.form.DisplayField
30355  * @extends Roo.form.Field
30356  * A generic Field to display non-editable data.
30357  * @constructor
30358  * Creates a new Display Field item.
30359  * @param {Object} config Configuration options
30360  */
30361 Roo.form.DisplayField = function(config){
30362     Roo.form.DisplayField.superclass.constructor.call(this, config);
30363     
30364 };
30365
30366 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30367     inputType:      'hidden',
30368     allowBlank:     true,
30369     readOnly:         true,
30370     
30371  
30372     /**
30373      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30374      */
30375     focusClass : undefined,
30376     /**
30377      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30378      */
30379     fieldClass: 'x-form-field',
30380     
30381      /**
30382      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30383      */
30384     valueRenderer: undefined,
30385     
30386     width: 100,
30387     /**
30388      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30389      * {tag: "input", type: "checkbox", autocomplete: "off"})
30390      */
30391      
30392  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30393
30394     onResize : function(){
30395         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30396         
30397     },
30398
30399     initEvents : function(){
30400         // Roo.form.Checkbox.superclass.initEvents.call(this);
30401         // has no events...
30402        
30403     },
30404
30405
30406     getResizeEl : function(){
30407         return this.wrap;
30408     },
30409
30410     getPositionEl : function(){
30411         return this.wrap;
30412     },
30413
30414     // private
30415     onRender : function(ct, position){
30416         
30417         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30418         //if(this.inputValue !== undefined){
30419         this.wrap = this.el.wrap();
30420         
30421         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30422         
30423         if (this.bodyStyle) {
30424             this.viewEl.applyStyles(this.bodyStyle);
30425         }
30426         //this.viewEl.setStyle('padding', '2px');
30427         
30428         this.setValue(this.value);
30429         
30430     },
30431 /*
30432     // private
30433     initValue : Roo.emptyFn,
30434
30435   */
30436
30437         // private
30438     onClick : function(){
30439         
30440     },
30441
30442     /**
30443      * Sets the checked state of the checkbox.
30444      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30445      */
30446     setValue : function(v){
30447         this.value = v;
30448         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30449         // this might be called before we have a dom element..
30450         if (!this.viewEl) {
30451             return;
30452         }
30453         this.viewEl.dom.innerHTML = html;
30454         Roo.form.DisplayField.superclass.setValue.call(this, v);
30455
30456     }
30457 });/*
30458  * 
30459  * Licence- LGPL
30460  * 
30461  */
30462
30463 /**
30464  * @class Roo.form.DayPicker
30465  * @extends Roo.form.Field
30466  * A Day picker show [M] [T] [W] ....
30467  * @constructor
30468  * Creates a new Day Picker
30469  * @param {Object} config Configuration options
30470  */
30471 Roo.form.DayPicker= function(config){
30472     Roo.form.DayPicker.superclass.constructor.call(this, config);
30473      
30474 };
30475
30476 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30477     /**
30478      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30479      */
30480     focusClass : undefined,
30481     /**
30482      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30483      */
30484     fieldClass: "x-form-field",
30485    
30486     /**
30487      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30488      * {tag: "input", type: "checkbox", autocomplete: "off"})
30489      */
30490     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30491     
30492    
30493     actionMode : 'viewEl', 
30494     //
30495     // private
30496  
30497     inputType : 'hidden',
30498     
30499      
30500     inputElement: false, // real input element?
30501     basedOn: false, // ????
30502     
30503     isFormField: true, // not sure where this is needed!!!!
30504
30505     onResize : function(){
30506         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30507         if(!this.boxLabel){
30508             this.el.alignTo(this.wrap, 'c-c');
30509         }
30510     },
30511
30512     initEvents : function(){
30513         Roo.form.Checkbox.superclass.initEvents.call(this);
30514         this.el.on("click", this.onClick,  this);
30515         this.el.on("change", this.onClick,  this);
30516     },
30517
30518
30519     getResizeEl : function(){
30520         return this.wrap;
30521     },
30522
30523     getPositionEl : function(){
30524         return this.wrap;
30525     },
30526
30527     
30528     // private
30529     onRender : function(ct, position){
30530         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30531        
30532         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30533         
30534         var r1 = '<table><tr>';
30535         var r2 = '<tr class="x-form-daypick-icons">';
30536         for (var i=0; i < 7; i++) {
30537             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30538             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30539         }
30540         
30541         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30542         viewEl.select('img').on('click', this.onClick, this);
30543         this.viewEl = viewEl;   
30544         
30545         
30546         // this will not work on Chrome!!!
30547         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30548         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30549         
30550         
30551           
30552
30553     },
30554
30555     // private
30556     initValue : Roo.emptyFn,
30557
30558     /**
30559      * Returns the checked state of the checkbox.
30560      * @return {Boolean} True if checked, else false
30561      */
30562     getValue : function(){
30563         return this.el.dom.value;
30564         
30565     },
30566
30567         // private
30568     onClick : function(e){ 
30569         //this.setChecked(!this.checked);
30570         Roo.get(e.target).toggleClass('x-menu-item-checked');
30571         this.refreshValue();
30572         //if(this.el.dom.checked != this.checked){
30573         //    this.setValue(this.el.dom.checked);
30574        // }
30575     },
30576     
30577     // private
30578     refreshValue : function()
30579     {
30580         var val = '';
30581         this.viewEl.select('img',true).each(function(e,i,n)  {
30582             val += e.is(".x-menu-item-checked") ? String(n) : '';
30583         });
30584         this.setValue(val, true);
30585     },
30586
30587     /**
30588      * Sets the checked state of the checkbox.
30589      * On is always based on a string comparison between inputValue and the param.
30590      * @param {Boolean/String} value - the value to set 
30591      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30592      */
30593     setValue : function(v,suppressEvent){
30594         if (!this.el.dom) {
30595             return;
30596         }
30597         var old = this.el.dom.value ;
30598         this.el.dom.value = v;
30599         if (suppressEvent) {
30600             return ;
30601         }
30602          
30603         // update display..
30604         this.viewEl.select('img',true).each(function(e,i,n)  {
30605             
30606             var on = e.is(".x-menu-item-checked");
30607             var newv = v.indexOf(String(n)) > -1;
30608             if (on != newv) {
30609                 e.toggleClass('x-menu-item-checked');
30610             }
30611             
30612         });
30613         
30614         
30615         this.fireEvent('change', this, v, old);
30616         
30617         
30618     },
30619    
30620     // handle setting of hidden value by some other method!!?!?
30621     setFromHidden: function()
30622     {
30623         if(!this.el){
30624             return;
30625         }
30626         //console.log("SET FROM HIDDEN");
30627         //alert('setFrom hidden');
30628         this.setValue(this.el.dom.value);
30629     },
30630     
30631     onDestroy : function()
30632     {
30633         if(this.viewEl){
30634             Roo.get(this.viewEl).remove();
30635         }
30636          
30637         Roo.form.DayPicker.superclass.onDestroy.call(this);
30638     }
30639
30640 });/*
30641  * RooJS Library 1.1.1
30642  * Copyright(c) 2008-2011  Alan Knowles
30643  *
30644  * License - LGPL
30645  */
30646  
30647
30648 /**
30649  * @class Roo.form.ComboCheck
30650  * @extends Roo.form.ComboBox
30651  * A combobox for multiple select items.
30652  *
30653  * FIXME - could do with a reset button..
30654  * 
30655  * @constructor
30656  * Create a new ComboCheck
30657  * @param {Object} config Configuration options
30658  */
30659 Roo.form.ComboCheck = function(config){
30660     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30661     // should verify some data...
30662     // like
30663     // hiddenName = required..
30664     // displayField = required
30665     // valudField == required
30666     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30667     var _t = this;
30668     Roo.each(req, function(e) {
30669         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30670             throw "Roo.form.ComboCheck : missing value for: " + e;
30671         }
30672     });
30673     
30674     
30675 };
30676
30677 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30678      
30679      
30680     editable : false,
30681      
30682     selectedClass: 'x-menu-item-checked', 
30683     
30684     // private
30685     onRender : function(ct, position){
30686         var _t = this;
30687         
30688         
30689         
30690         if(!this.tpl){
30691             var cls = 'x-combo-list';
30692
30693             
30694             this.tpl =  new Roo.Template({
30695                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30696                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30697                    '<span>{' + this.displayField + '}</span>' +
30698                     '</div>' 
30699                 
30700             });
30701         }
30702  
30703         
30704         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30705         this.view.singleSelect = false;
30706         this.view.multiSelect = true;
30707         this.view.toggleSelect = true;
30708         this.pageTb.add(new Roo.Toolbar.Fill(), {
30709             
30710             text: 'Done',
30711             handler: function()
30712             {
30713                 _t.collapse();
30714             }
30715         });
30716     },
30717     
30718     onViewOver : function(e, t){
30719         // do nothing...
30720         return;
30721         
30722     },
30723     
30724     onViewClick : function(doFocus,index){
30725         return;
30726         
30727     },
30728     select: function () {
30729         //Roo.log("SELECT CALLED");
30730     },
30731      
30732     selectByValue : function(xv, scrollIntoView){
30733         var ar = this.getValueArray();
30734         var sels = [];
30735         
30736         Roo.each(ar, function(v) {
30737             if(v === undefined || v === null){
30738                 return;
30739             }
30740             var r = this.findRecord(this.valueField, v);
30741             if(r){
30742                 sels.push(this.store.indexOf(r))
30743                 
30744             }
30745         },this);
30746         this.view.select(sels);
30747         return false;
30748     },
30749     
30750     
30751     
30752     onSelect : function(record, index){
30753        // Roo.log("onselect Called");
30754        // this is only called by the clear button now..
30755         this.view.clearSelections();
30756         this.setValue('[]');
30757         if (this.value != this.valueBefore) {
30758             this.fireEvent('change', this, this.value, this.valueBefore);
30759             this.valueBefore = this.value;
30760         }
30761     },
30762     getValueArray : function()
30763     {
30764         var ar = [] ;
30765         
30766         try {
30767             //Roo.log(this.value);
30768             if (typeof(this.value) == 'undefined') {
30769                 return [];
30770             }
30771             var ar = Roo.decode(this.value);
30772             return  ar instanceof Array ? ar : []; //?? valid?
30773             
30774         } catch(e) {
30775             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30776             return [];
30777         }
30778          
30779     },
30780     expand : function ()
30781     {
30782         
30783         Roo.form.ComboCheck.superclass.expand.call(this);
30784         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30785         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30786         
30787
30788     },
30789     
30790     collapse : function(){
30791         Roo.form.ComboCheck.superclass.collapse.call(this);
30792         var sl = this.view.getSelectedIndexes();
30793         var st = this.store;
30794         var nv = [];
30795         var tv = [];
30796         var r;
30797         Roo.each(sl, function(i) {
30798             r = st.getAt(i);
30799             nv.push(r.get(this.valueField));
30800         },this);
30801         this.setValue(Roo.encode(nv));
30802         if (this.value != this.valueBefore) {
30803
30804             this.fireEvent('change', this, this.value, this.valueBefore);
30805             this.valueBefore = this.value;
30806         }
30807         
30808     },
30809     
30810     setValue : function(v){
30811         // Roo.log(v);
30812         this.value = v;
30813         
30814         var vals = this.getValueArray();
30815         var tv = [];
30816         Roo.each(vals, function(k) {
30817             var r = this.findRecord(this.valueField, k);
30818             if(r){
30819                 tv.push(r.data[this.displayField]);
30820             }else if(this.valueNotFoundText !== undefined){
30821                 tv.push( this.valueNotFoundText );
30822             }
30823         },this);
30824        // Roo.log(tv);
30825         
30826         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30827         this.hiddenField.value = v;
30828         this.value = v;
30829     }
30830     
30831 });/*
30832  * Based on:
30833  * Ext JS Library 1.1.1
30834  * Copyright(c) 2006-2007, Ext JS, LLC.
30835  *
30836  * Originally Released Under LGPL - original licence link has changed is not relivant.
30837  *
30838  * Fork - LGPL
30839  * <script type="text/javascript">
30840  */
30841  
30842 /**
30843  * @class Roo.form.Signature
30844  * @extends Roo.form.Field
30845  * Signature field.  
30846  * @constructor
30847  * 
30848  * @param {Object} config Configuration options
30849  */
30850
30851 Roo.form.Signature = function(config){
30852     Roo.form.Signature.superclass.constructor.call(this, config);
30853     
30854     this.addEvents({// not in used??
30855          /**
30856          * @event confirm
30857          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30858              * @param {Roo.form.Signature} combo This combo box
30859              */
30860         'confirm' : true,
30861         /**
30862          * @event reset
30863          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30864              * @param {Roo.form.ComboBox} combo This combo box
30865              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30866              */
30867         'reset' : true
30868     });
30869 };
30870
30871 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30872     /**
30873      * @cfg {Object} labels Label to use when rendering a form.
30874      * defaults to 
30875      * labels : { 
30876      *      clear : "Clear",
30877      *      confirm : "Confirm"
30878      *  }
30879      */
30880     labels : { 
30881         clear : "Clear",
30882         confirm : "Confirm"
30883     },
30884     /**
30885      * @cfg {Number} width The signature panel width (defaults to 300)
30886      */
30887     width: 300,
30888     /**
30889      * @cfg {Number} height The signature panel height (defaults to 100)
30890      */
30891     height : 100,
30892     /**
30893      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30894      */
30895     allowBlank : false,
30896     
30897     //private
30898     // {Object} signPanel The signature SVG panel element (defaults to {})
30899     signPanel : {},
30900     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30901     isMouseDown : false,
30902     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30903     isConfirmed : false,
30904     // {String} signatureTmp SVG mapping string (defaults to empty string)
30905     signatureTmp : '',
30906     
30907     
30908     defaultAutoCreate : { // modified by initCompnoent..
30909         tag: "input",
30910         type:"hidden"
30911     },
30912
30913     // private
30914     onRender : function(ct, position){
30915         
30916         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30917         
30918         this.wrap = this.el.wrap({
30919             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30920         });
30921         
30922         this.createToolbar(this);
30923         this.signPanel = this.wrap.createChild({
30924                 tag: 'div',
30925                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30926             }, this.el
30927         );
30928             
30929         this.svgID = Roo.id();
30930         this.svgEl = this.signPanel.createChild({
30931               xmlns : 'http://www.w3.org/2000/svg',
30932               tag : 'svg',
30933               id : this.svgID + "-svg",
30934               width: this.width,
30935               height: this.height,
30936               viewBox: '0 0 '+this.width+' '+this.height,
30937               cn : [
30938                 {
30939                     tag: "rect",
30940                     id: this.svgID + "-svg-r",
30941                     width: this.width,
30942                     height: this.height,
30943                     fill: "#ffa"
30944                 },
30945                 {
30946                     tag: "line",
30947                     id: this.svgID + "-svg-l",
30948                     x1: "0", // start
30949                     y1: (this.height*0.8), // start set the line in 80% of height
30950                     x2: this.width, // end
30951                     y2: (this.height*0.8), // end set the line in 80% of height
30952                     'stroke': "#666",
30953                     'stroke-width': "1",
30954                     'stroke-dasharray': "3",
30955                     'shape-rendering': "crispEdges",
30956                     'pointer-events': "none"
30957                 },
30958                 {
30959                     tag: "path",
30960                     id: this.svgID + "-svg-p",
30961                     'stroke': "navy",
30962                     'stroke-width': "3",
30963                     'fill': "none",
30964                     'pointer-events': 'none'
30965                 }
30966               ]
30967         });
30968         this.createSVG();
30969         this.svgBox = this.svgEl.dom.getScreenCTM();
30970     },
30971     createSVG : function(){ 
30972         var svg = this.signPanel;
30973         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30974         var t = this;
30975
30976         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30977         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30978         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30979         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30980         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30981         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30982         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30983         
30984     },
30985     isTouchEvent : function(e){
30986         return e.type.match(/^touch/);
30987     },
30988     getCoords : function (e) {
30989         var pt    = this.svgEl.dom.createSVGPoint();
30990         pt.x = e.clientX; 
30991         pt.y = e.clientY;
30992         if (this.isTouchEvent(e)) {
30993             pt.x =  e.targetTouches[0].clientX 
30994             pt.y = e.targetTouches[0].clientY;
30995         }
30996         var a = this.svgEl.dom.getScreenCTM();
30997         var b = a.inverse();
30998         var mx = pt.matrixTransform(b);
30999         return mx.x + ',' + mx.y;
31000     },
31001     //mouse event headler 
31002     down : function (e) {
31003         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31004         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31005         
31006         this.isMouseDown = true;
31007         
31008         e.preventDefault();
31009     },
31010     move : function (e) {
31011         if (this.isMouseDown) {
31012             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31013             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31014         }
31015         
31016         e.preventDefault();
31017     },
31018     up : function (e) {
31019         this.isMouseDown = false;
31020         var sp = this.signatureTmp.split(' ');
31021         
31022         if(sp.length > 1){
31023             if(!sp[sp.length-2].match(/^L/)){
31024                 sp.pop();
31025                 sp.pop();
31026                 sp.push("");
31027                 this.signatureTmp = sp.join(" ");
31028             }
31029         }
31030         if(this.getValue() != this.signatureTmp){
31031             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31032             this.isConfirmed = false;
31033         }
31034         e.preventDefault();
31035     },
31036     
31037     /**
31038      * Protected method that will not generally be called directly. It
31039      * is called when the editor creates its toolbar. Override this method if you need to
31040      * add custom toolbar buttons.
31041      * @param {HtmlEditor} editor
31042      */
31043     createToolbar : function(editor){
31044          function btn(id, toggle, handler){
31045             var xid = fid + '-'+ id ;
31046             return {
31047                 id : xid,
31048                 cmd : id,
31049                 cls : 'x-btn-icon x-edit-'+id,
31050                 enableToggle:toggle !== false,
31051                 scope: editor, // was editor...
31052                 handler:handler||editor.relayBtnCmd,
31053                 clickEvent:'mousedown',
31054                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31055                 tabIndex:-1
31056             };
31057         }
31058         
31059         
31060         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31061         this.tb = tb;
31062         this.tb.add(
31063            {
31064                 cls : ' x-signature-btn x-signature-'+id,
31065                 scope: editor, // was editor...
31066                 handler: this.reset,
31067                 clickEvent:'mousedown',
31068                 text: this.labels.clear
31069             },
31070             {
31071                  xtype : 'Fill',
31072                  xns: Roo.Toolbar
31073             }, 
31074             {
31075                 cls : '  x-signature-btn x-signature-'+id,
31076                 scope: editor, // was editor...
31077                 handler: this.confirmHandler,
31078                 clickEvent:'mousedown',
31079                 text: this.labels.confirm
31080             }
31081         );
31082     
31083     },
31084     //public
31085     /**
31086      * when user is clicked confirm then show this image.....
31087      * 
31088      * @return {String} Image Data URI
31089      */
31090     getImageDataURI : function(){
31091         var svg = this.svgEl.dom.parentNode.innerHTML;
31092         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31093         return src; 
31094     },
31095     /**
31096      * 
31097      * @return {Boolean} this.isConfirmed
31098      */
31099     getConfirmed : function(){
31100         return this.isConfirmed;
31101     },
31102     /**
31103      * 
31104      * @return {Number} this.width
31105      */
31106     getWidth : function(){
31107         return this.width;
31108     },
31109     /**
31110      * 
31111      * @return {Number} this.height
31112      */
31113     getHeight : function(){
31114         return this.height;
31115     },
31116     // private
31117     getSignature : function(){
31118         return this.signatureTmp;
31119     },
31120     // private
31121     reset : function(){
31122         this.signatureTmp = '';
31123         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31124         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31125         this.isConfirmed = false;
31126         Roo.form.Signature.superclass.reset.call(this);
31127     },
31128     setSignature : function(s){
31129         this.signatureTmp = s;
31130         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31131         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31132         this.setValue(s);
31133         this.isConfirmed = false;
31134         Roo.form.Signature.superclass.reset.call(this);
31135     }, 
31136     test : function(){
31137 //        Roo.log(this.signPanel.dom.contentWindow.up())
31138     },
31139     //private
31140     setConfirmed : function(){
31141         
31142         
31143         
31144 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31145     },
31146     // private
31147     confirmHandler : function(){
31148         if(!this.getSignature()){
31149             return;
31150         }
31151         
31152         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31153         this.setValue(this.getSignature());
31154         this.isConfirmed = true;
31155         
31156         this.fireEvent('confirm', this);
31157     },
31158     // private
31159     // Subclasses should provide the validation implementation by overriding this
31160     validateValue : function(value){
31161         if(this.allowBlank){
31162             return true;
31163         }
31164         
31165         if(this.isConfirmed){
31166             return true;
31167         }
31168         return false;
31169     }
31170 });/*
31171  * Based on:
31172  * Ext JS Library 1.1.1
31173  * Copyright(c) 2006-2007, Ext JS, LLC.
31174  *
31175  * Originally Released Under LGPL - original licence link has changed is not relivant.
31176  *
31177  * Fork - LGPL
31178  * <script type="text/javascript">
31179  */
31180  
31181
31182 /**
31183  * @class Roo.form.ComboBox
31184  * @extends Roo.form.TriggerField
31185  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31186  * @constructor
31187  * Create a new ComboBox.
31188  * @param {Object} config Configuration options
31189  */
31190 Roo.form.Select = function(config){
31191     Roo.form.Select.superclass.constructor.call(this, config);
31192      
31193 };
31194
31195 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31196     /**
31197      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31198      */
31199     /**
31200      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31201      * rendering into an Roo.Editor, defaults to false)
31202      */
31203     /**
31204      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31205      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31206      */
31207     /**
31208      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31209      */
31210     /**
31211      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31212      * the dropdown list (defaults to undefined, with no header element)
31213      */
31214
31215      /**
31216      * @cfg {String/Roo.Template} tpl The template to use to render the output
31217      */
31218      
31219     // private
31220     defaultAutoCreate : {tag: "select"  },
31221     /**
31222      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31223      */
31224     listWidth: undefined,
31225     /**
31226      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31227      * mode = 'remote' or 'text' if mode = 'local')
31228      */
31229     displayField: undefined,
31230     /**
31231      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31232      * mode = 'remote' or 'value' if mode = 'local'). 
31233      * Note: use of a valueField requires the user make a selection
31234      * in order for a value to be mapped.
31235      */
31236     valueField: undefined,
31237     
31238     
31239     /**
31240      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31241      * field's data value (defaults to the underlying DOM element's name)
31242      */
31243     hiddenName: undefined,
31244     /**
31245      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31246      */
31247     listClass: '',
31248     /**
31249      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31250      */
31251     selectedClass: 'x-combo-selected',
31252     /**
31253      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31254      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31255      * which displays a downward arrow icon).
31256      */
31257     triggerClass : 'x-form-arrow-trigger',
31258     /**
31259      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31260      */
31261     shadow:'sides',
31262     /**
31263      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31264      * anchor positions (defaults to 'tl-bl')
31265      */
31266     listAlign: 'tl-bl?',
31267     /**
31268      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31269      */
31270     maxHeight: 300,
31271     /**
31272      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31273      * query specified by the allQuery config option (defaults to 'query')
31274      */
31275     triggerAction: 'query',
31276     /**
31277      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31278      * (defaults to 4, does not apply if editable = false)
31279      */
31280     minChars : 4,
31281     /**
31282      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31283      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31284      */
31285     typeAhead: false,
31286     /**
31287      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31288      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31289      */
31290     queryDelay: 500,
31291     /**
31292      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31293      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31294      */
31295     pageSize: 0,
31296     /**
31297      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31298      * when editable = true (defaults to false)
31299      */
31300     selectOnFocus:false,
31301     /**
31302      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31303      */
31304     queryParam: 'query',
31305     /**
31306      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31307      * when mode = 'remote' (defaults to 'Loading...')
31308      */
31309     loadingText: 'Loading...',
31310     /**
31311      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31312      */
31313     resizable: false,
31314     /**
31315      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31316      */
31317     handleHeight : 8,
31318     /**
31319      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31320      * traditional select (defaults to true)
31321      */
31322     editable: true,
31323     /**
31324      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31325      */
31326     allQuery: '',
31327     /**
31328      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31329      */
31330     mode: 'remote',
31331     /**
31332      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31333      * listWidth has a higher value)
31334      */
31335     minListWidth : 70,
31336     /**
31337      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31338      * allow the user to set arbitrary text into the field (defaults to false)
31339      */
31340     forceSelection:false,
31341     /**
31342      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31343      * if typeAhead = true (defaults to 250)
31344      */
31345     typeAheadDelay : 250,
31346     /**
31347      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31348      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31349      */
31350     valueNotFoundText : undefined,
31351     
31352     /**
31353      * @cfg {String} defaultValue The value displayed after loading the store.
31354      */
31355     defaultValue: '',
31356     
31357     /**
31358      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31359      */
31360     blockFocus : false,
31361     
31362     /**
31363      * @cfg {Boolean} disableClear Disable showing of clear button.
31364      */
31365     disableClear : false,
31366     /**
31367      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31368      */
31369     alwaysQuery : false,
31370     
31371     //private
31372     addicon : false,
31373     editicon: false,
31374     
31375     // element that contains real text value.. (when hidden is used..)
31376      
31377     // private
31378     onRender : function(ct, position){
31379         Roo.form.Field.prototype.onRender.call(this, ct, position);
31380         
31381         if(this.store){
31382             this.store.on('beforeload', this.onBeforeLoad, this);
31383             this.store.on('load', this.onLoad, this);
31384             this.store.on('loadexception', this.onLoadException, this);
31385             this.store.load({});
31386         }
31387         
31388         
31389         
31390     },
31391
31392     // private
31393     initEvents : function(){
31394         //Roo.form.ComboBox.superclass.initEvents.call(this);
31395  
31396     },
31397
31398     onDestroy : function(){
31399        
31400         if(this.store){
31401             this.store.un('beforeload', this.onBeforeLoad, this);
31402             this.store.un('load', this.onLoad, this);
31403             this.store.un('loadexception', this.onLoadException, this);
31404         }
31405         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31406     },
31407
31408     // private
31409     fireKey : function(e){
31410         if(e.isNavKeyPress() && !this.list.isVisible()){
31411             this.fireEvent("specialkey", this, e);
31412         }
31413     },
31414
31415     // private
31416     onResize: function(w, h){
31417         
31418         return; 
31419     
31420         
31421     },
31422
31423     /**
31424      * Allow or prevent the user from directly editing the field text.  If false is passed,
31425      * the user will only be able to select from the items defined in the dropdown list.  This method
31426      * is the runtime equivalent of setting the 'editable' config option at config time.
31427      * @param {Boolean} value True to allow the user to directly edit the field text
31428      */
31429     setEditable : function(value){
31430          
31431     },
31432
31433     // private
31434     onBeforeLoad : function(){
31435         
31436         Roo.log("Select before load");
31437         return;
31438     
31439         this.innerList.update(this.loadingText ?
31440                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31441         //this.restrictHeight();
31442         this.selectedIndex = -1;
31443     },
31444
31445     // private
31446     onLoad : function(){
31447
31448     
31449         var dom = this.el.dom;
31450         dom.innerHTML = '';
31451          var od = dom.ownerDocument;
31452          
31453         if (this.emptyText) {
31454             var op = od.createElement('option');
31455             op.setAttribute('value', '');
31456             op.innerHTML = String.format('{0}', this.emptyText);
31457             dom.appendChild(op);
31458         }
31459         if(this.store.getCount() > 0){
31460            
31461             var vf = this.valueField;
31462             var df = this.displayField;
31463             this.store.data.each(function(r) {
31464                 // which colmsn to use... testing - cdoe / title..
31465                 var op = od.createElement('option');
31466                 op.setAttribute('value', r.data[vf]);
31467                 op.innerHTML = String.format('{0}', r.data[df]);
31468                 dom.appendChild(op);
31469             });
31470             if (typeof(this.defaultValue != 'undefined')) {
31471                 this.setValue(this.defaultValue);
31472             }
31473             
31474              
31475         }else{
31476             //this.onEmptyResults();
31477         }
31478         //this.el.focus();
31479     },
31480     // private
31481     onLoadException : function()
31482     {
31483         dom.innerHTML = '';
31484             
31485         Roo.log("Select on load exception");
31486         return;
31487     
31488         this.collapse();
31489         Roo.log(this.store.reader.jsonData);
31490         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31491             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31492         }
31493         
31494         
31495     },
31496     // private
31497     onTypeAhead : function(){
31498          
31499     },
31500
31501     // private
31502     onSelect : function(record, index){
31503         Roo.log('on select?');
31504         return;
31505         if(this.fireEvent('beforeselect', this, record, index) !== false){
31506             this.setFromData(index > -1 ? record.data : false);
31507             this.collapse();
31508             this.fireEvent('select', this, record, index);
31509         }
31510     },
31511
31512     /**
31513      * Returns the currently selected field value or empty string if no value is set.
31514      * @return {String} value The selected value
31515      */
31516     getValue : function(){
31517         var dom = this.el.dom;
31518         this.value = dom.options[dom.selectedIndex].value;
31519         return this.value;
31520         
31521     },
31522
31523     /**
31524      * Clears any text/value currently set in the field
31525      */
31526     clearValue : function(){
31527         this.value = '';
31528         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31529         
31530     },
31531
31532     /**
31533      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31534      * will be displayed in the field.  If the value does not match the data value of an existing item,
31535      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31536      * Otherwise the field will be blank (although the value will still be set).
31537      * @param {String} value The value to match
31538      */
31539     setValue : function(v){
31540         var d = this.el.dom;
31541         for (var i =0; i < d.options.length;i++) {
31542             if (v == d.options[i].value) {
31543                 d.selectedIndex = i;
31544                 this.value = v;
31545                 return;
31546             }
31547         }
31548         this.clearValue();
31549     },
31550     /**
31551      * @property {Object} the last set data for the element
31552      */
31553     
31554     lastData : false,
31555     /**
31556      * Sets the value of the field based on a object which is related to the record format for the store.
31557      * @param {Object} value the value to set as. or false on reset?
31558      */
31559     setFromData : function(o){
31560         Roo.log('setfrom data?');
31561          
31562         
31563         
31564     },
31565     // private
31566     reset : function(){
31567         this.clearValue();
31568     },
31569     // private
31570     findRecord : function(prop, value){
31571         
31572         return false;
31573     
31574         var record;
31575         if(this.store.getCount() > 0){
31576             this.store.each(function(r){
31577                 if(r.data[prop] == value){
31578                     record = r;
31579                     return false;
31580                 }
31581                 return true;
31582             });
31583         }
31584         return record;
31585     },
31586     
31587     getName: function()
31588     {
31589         // returns hidden if it's set..
31590         if (!this.rendered) {return ''};
31591         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31592         
31593     },
31594      
31595
31596     
31597
31598     // private
31599     onEmptyResults : function(){
31600         Roo.log('empty results');
31601         //this.collapse();
31602     },
31603
31604     /**
31605      * Returns true if the dropdown list is expanded, else false.
31606      */
31607     isExpanded : function(){
31608         return false;
31609     },
31610
31611     /**
31612      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31613      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31614      * @param {String} value The data value of the item to select
31615      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31616      * selected item if it is not currently in view (defaults to true)
31617      * @return {Boolean} True if the value matched an item in the list, else false
31618      */
31619     selectByValue : function(v, scrollIntoView){
31620         Roo.log('select By Value');
31621         return false;
31622     
31623         if(v !== undefined && v !== null){
31624             var r = this.findRecord(this.valueField || this.displayField, v);
31625             if(r){
31626                 this.select(this.store.indexOf(r), scrollIntoView);
31627                 return true;
31628             }
31629         }
31630         return false;
31631     },
31632
31633     /**
31634      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31635      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31636      * @param {Number} index The zero-based index of the list item to select
31637      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31638      * selected item if it is not currently in view (defaults to true)
31639      */
31640     select : function(index, scrollIntoView){
31641         Roo.log('select ');
31642         return  ;
31643         
31644         this.selectedIndex = index;
31645         this.view.select(index);
31646         if(scrollIntoView !== false){
31647             var el = this.view.getNode(index);
31648             if(el){
31649                 this.innerList.scrollChildIntoView(el, false);
31650             }
31651         }
31652     },
31653
31654       
31655
31656     // private
31657     validateBlur : function(){
31658         
31659         return;
31660         
31661     },
31662
31663     // private
31664     initQuery : function(){
31665         this.doQuery(this.getRawValue());
31666     },
31667
31668     // private
31669     doForce : function(){
31670         if(this.el.dom.value.length > 0){
31671             this.el.dom.value =
31672                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31673              
31674         }
31675     },
31676
31677     /**
31678      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31679      * query allowing the query action to be canceled if needed.
31680      * @param {String} query The SQL query to execute
31681      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31682      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31683      * saved in the current store (defaults to false)
31684      */
31685     doQuery : function(q, forceAll){
31686         
31687         Roo.log('doQuery?');
31688         if(q === undefined || q === null){
31689             q = '';
31690         }
31691         var qe = {
31692             query: q,
31693             forceAll: forceAll,
31694             combo: this,
31695             cancel:false
31696         };
31697         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31698             return false;
31699         }
31700         q = qe.query;
31701         forceAll = qe.forceAll;
31702         if(forceAll === true || (q.length >= this.minChars)){
31703             if(this.lastQuery != q || this.alwaysQuery){
31704                 this.lastQuery = q;
31705                 if(this.mode == 'local'){
31706                     this.selectedIndex = -1;
31707                     if(forceAll){
31708                         this.store.clearFilter();
31709                     }else{
31710                         this.store.filter(this.displayField, q);
31711                     }
31712                     this.onLoad();
31713                 }else{
31714                     this.store.baseParams[this.queryParam] = q;
31715                     this.store.load({
31716                         params: this.getParams(q)
31717                     });
31718                     this.expand();
31719                 }
31720             }else{
31721                 this.selectedIndex = -1;
31722                 this.onLoad();   
31723             }
31724         }
31725     },
31726
31727     // private
31728     getParams : function(q){
31729         var p = {};
31730         //p[this.queryParam] = q;
31731         if(this.pageSize){
31732             p.start = 0;
31733             p.limit = this.pageSize;
31734         }
31735         return p;
31736     },
31737
31738     /**
31739      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31740      */
31741     collapse : function(){
31742         
31743     },
31744
31745     // private
31746     collapseIf : function(e){
31747         
31748     },
31749
31750     /**
31751      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31752      */
31753     expand : function(){
31754         
31755     } ,
31756
31757     // private
31758      
31759
31760     /** 
31761     * @cfg {Boolean} grow 
31762     * @hide 
31763     */
31764     /** 
31765     * @cfg {Number} growMin 
31766     * @hide 
31767     */
31768     /** 
31769     * @cfg {Number} growMax 
31770     * @hide 
31771     */
31772     /**
31773      * @hide
31774      * @method autoSize
31775      */
31776     
31777     setWidth : function()
31778     {
31779         
31780     },
31781     getResizeEl : function(){
31782         return this.el;
31783     }
31784 });//<script type="text/javasscript">
31785  
31786
31787 /**
31788  * @class Roo.DDView
31789  * A DnD enabled version of Roo.View.
31790  * @param {Element/String} container The Element in which to create the View.
31791  * @param {String} tpl The template string used to create the markup for each element of the View
31792  * @param {Object} config The configuration properties. These include all the config options of
31793  * {@link Roo.View} plus some specific to this class.<br>
31794  * <p>
31795  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31796  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31797  * <p>
31798  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31799 .x-view-drag-insert-above {
31800         border-top:1px dotted #3366cc;
31801 }
31802 .x-view-drag-insert-below {
31803         border-bottom:1px dotted #3366cc;
31804 }
31805 </code></pre>
31806  * 
31807  */
31808  
31809 Roo.DDView = function(container, tpl, config) {
31810     Roo.DDView.superclass.constructor.apply(this, arguments);
31811     this.getEl().setStyle("outline", "0px none");
31812     this.getEl().unselectable();
31813     if (this.dragGroup) {
31814                 this.setDraggable(this.dragGroup.split(","));
31815     }
31816     if (this.dropGroup) {
31817                 this.setDroppable(this.dropGroup.split(","));
31818     }
31819     if (this.deletable) {
31820         this.setDeletable();
31821     }
31822     this.isDirtyFlag = false;
31823         this.addEvents({
31824                 "drop" : true
31825         });
31826 };
31827
31828 Roo.extend(Roo.DDView, Roo.View, {
31829 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31830 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31831 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31832 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31833
31834         isFormField: true,
31835
31836         reset: Roo.emptyFn,
31837         
31838         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31839
31840         validate: function() {
31841                 return true;
31842         },
31843         
31844         destroy: function() {
31845                 this.purgeListeners();
31846                 this.getEl.removeAllListeners();
31847                 this.getEl().remove();
31848                 if (this.dragZone) {
31849                         if (this.dragZone.destroy) {
31850                                 this.dragZone.destroy();
31851                         }
31852                 }
31853                 if (this.dropZone) {
31854                         if (this.dropZone.destroy) {
31855                                 this.dropZone.destroy();
31856                         }
31857                 }
31858         },
31859
31860 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31861         getName: function() {
31862                 return this.name;
31863         },
31864
31865 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31866         setValue: function(v) {
31867                 if (!this.store) {
31868                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31869                 }
31870                 var data = {};
31871                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31872                 this.store.proxy = new Roo.data.MemoryProxy(data);
31873                 this.store.load();
31874         },
31875
31876 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31877         getValue: function() {
31878                 var result = '(';
31879                 this.store.each(function(rec) {
31880                         result += rec.id + ',';
31881                 });
31882                 return result.substr(0, result.length - 1) + ')';
31883         },
31884         
31885         getIds: function() {
31886                 var i = 0, result = new Array(this.store.getCount());
31887                 this.store.each(function(rec) {
31888                         result[i++] = rec.id;
31889                 });
31890                 return result;
31891         },
31892         
31893         isDirty: function() {
31894                 return this.isDirtyFlag;
31895         },
31896
31897 /**
31898  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31899  *      whole Element becomes the target, and this causes the drop gesture to append.
31900  */
31901     getTargetFromEvent : function(e) {
31902                 var target = e.getTarget();
31903                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31904                 target = target.parentNode;
31905                 }
31906                 if (!target) {
31907                         target = this.el.dom.lastChild || this.el.dom;
31908                 }
31909                 return target;
31910     },
31911
31912 /**
31913  *      Create the drag data which consists of an object which has the property "ddel" as
31914  *      the drag proxy element. 
31915  */
31916     getDragData : function(e) {
31917         var target = this.findItemFromChild(e.getTarget());
31918                 if(target) {
31919                         this.handleSelection(e);
31920                         var selNodes = this.getSelectedNodes();
31921             var dragData = {
31922                 source: this,
31923                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31924                 nodes: selNodes,
31925                 records: []
31926                         };
31927                         var selectedIndices = this.getSelectedIndexes();
31928                         for (var i = 0; i < selectedIndices.length; i++) {
31929                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31930                         }
31931                         if (selNodes.length == 1) {
31932                                 dragData.ddel = target.cloneNode(true); // the div element
31933                         } else {
31934                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31935                                 div.className = 'multi-proxy';
31936                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31937                                         div.appendChild(selNodes[i].cloneNode(true));
31938                                 }
31939                                 dragData.ddel = div;
31940                         }
31941             //console.log(dragData)
31942             //console.log(dragData.ddel.innerHTML)
31943                         return dragData;
31944                 }
31945         //console.log('nodragData')
31946                 return false;
31947     },
31948     
31949 /**     Specify to which ddGroup items in this DDView may be dragged. */
31950     setDraggable: function(ddGroup) {
31951         if (ddGroup instanceof Array) {
31952                 Roo.each(ddGroup, this.setDraggable, this);
31953                 return;
31954         }
31955         if (this.dragZone) {
31956                 this.dragZone.addToGroup(ddGroup);
31957         } else {
31958                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31959                                 containerScroll: true,
31960                                 ddGroup: ddGroup 
31961
31962                         });
31963 //                      Draggability implies selection. DragZone's mousedown selects the element.
31964                         if (!this.multiSelect) { this.singleSelect = true; }
31965
31966 //                      Wire the DragZone's handlers up to methods in *this*
31967                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31968                 }
31969     },
31970
31971 /**     Specify from which ddGroup this DDView accepts drops. */
31972     setDroppable: function(ddGroup) {
31973         if (ddGroup instanceof Array) {
31974                 Roo.each(ddGroup, this.setDroppable, this);
31975                 return;
31976         }
31977         if (this.dropZone) {
31978                 this.dropZone.addToGroup(ddGroup);
31979         } else {
31980                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31981                                 containerScroll: true,
31982                                 ddGroup: ddGroup
31983                         });
31984
31985 //                      Wire the DropZone's handlers up to methods in *this*
31986                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31987                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31988                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31989                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31990                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31991                 }
31992     },
31993
31994 /**     Decide whether to drop above or below a View node. */
31995     getDropPoint : function(e, n, dd){
31996         if (n == this.el.dom) { return "above"; }
31997                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31998                 var c = t + (b - t) / 2;
31999                 var y = Roo.lib.Event.getPageY(e);
32000                 if(y <= c) {
32001                         return "above";
32002                 }else{
32003                         return "below";
32004                 }
32005     },
32006
32007     onNodeEnter : function(n, dd, e, data){
32008                 return false;
32009     },
32010     
32011     onNodeOver : function(n, dd, e, data){
32012                 var pt = this.getDropPoint(e, n, dd);
32013                 // set the insert point style on the target node
32014                 var dragElClass = this.dropNotAllowed;
32015                 if (pt) {
32016                         var targetElClass;
32017                         if (pt == "above"){
32018                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32019                                 targetElClass = "x-view-drag-insert-above";
32020                         } else {
32021                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32022                                 targetElClass = "x-view-drag-insert-below";
32023                         }
32024                         if (this.lastInsertClass != targetElClass){
32025                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32026                                 this.lastInsertClass = targetElClass;
32027                         }
32028                 }
32029                 return dragElClass;
32030         },
32031
32032     onNodeOut : function(n, dd, e, data){
32033                 this.removeDropIndicators(n);
32034     },
32035
32036     onNodeDrop : function(n, dd, e, data){
32037         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32038                 return false;
32039         }
32040         var pt = this.getDropPoint(e, n, dd);
32041                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32042                 if (pt == "below") { insertAt++; }
32043                 for (var i = 0; i < data.records.length; i++) {
32044                         var r = data.records[i];
32045                         var dup = this.store.getById(r.id);
32046                         if (dup && (dd != this.dragZone)) {
32047                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32048                         } else {
32049                                 if (data.copy) {
32050                                         this.store.insert(insertAt++, r.copy());
32051                                 } else {
32052                                         data.source.isDirtyFlag = true;
32053                                         r.store.remove(r);
32054                                         this.store.insert(insertAt++, r);
32055                                 }
32056                                 this.isDirtyFlag = true;
32057                         }
32058                 }
32059                 this.dragZone.cachedTarget = null;
32060                 return true;
32061     },
32062
32063     removeDropIndicators : function(n){
32064                 if(n){
32065                         Roo.fly(n).removeClass([
32066                                 "x-view-drag-insert-above",
32067                                 "x-view-drag-insert-below"]);
32068                         this.lastInsertClass = "_noclass";
32069                 }
32070     },
32071
32072 /**
32073  *      Utility method. Add a delete option to the DDView's context menu.
32074  *      @param {String} imageUrl The URL of the "delete" icon image.
32075  */
32076         setDeletable: function(imageUrl) {
32077                 if (!this.singleSelect && !this.multiSelect) {
32078                         this.singleSelect = true;
32079                 }
32080                 var c = this.getContextMenu();
32081                 this.contextMenu.on("itemclick", function(item) {
32082                         switch (item.id) {
32083                                 case "delete":
32084                                         this.remove(this.getSelectedIndexes());
32085                                         break;
32086                         }
32087                 }, this);
32088                 this.contextMenu.add({
32089                         icon: imageUrl,
32090                         id: "delete",
32091                         text: 'Delete'
32092                 });
32093         },
32094         
32095 /**     Return the context menu for this DDView. */
32096         getContextMenu: function() {
32097                 if (!this.contextMenu) {
32098 //                      Create the View's context menu
32099                         this.contextMenu = new Roo.menu.Menu({
32100                                 id: this.id + "-contextmenu"
32101                         });
32102                         this.el.on("contextmenu", this.showContextMenu, this);
32103                 }
32104                 return this.contextMenu;
32105         },
32106         
32107         disableContextMenu: function() {
32108                 if (this.contextMenu) {
32109                         this.el.un("contextmenu", this.showContextMenu, this);
32110                 }
32111         },
32112
32113         showContextMenu: function(e, item) {
32114         item = this.findItemFromChild(e.getTarget());
32115                 if (item) {
32116                         e.stopEvent();
32117                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32118                         this.contextMenu.showAt(e.getXY());
32119             }
32120     },
32121
32122 /**
32123  *      Remove {@link Roo.data.Record}s at the specified indices.
32124  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32125  */
32126     remove: function(selectedIndices) {
32127                 selectedIndices = [].concat(selectedIndices);
32128                 for (var i = 0; i < selectedIndices.length; i++) {
32129                         var rec = this.store.getAt(selectedIndices[i]);
32130                         this.store.remove(rec);
32131                 }
32132     },
32133
32134 /**
32135  *      Double click fires the event, but also, if this is draggable, and there is only one other
32136  *      related DropZone, it transfers the selected node.
32137  */
32138     onDblClick : function(e){
32139         var item = this.findItemFromChild(e.getTarget());
32140         if(item){
32141             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32142                 return false;
32143             }
32144             if (this.dragGroup) {
32145                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32146                     while (targets.indexOf(this.dropZone) > -1) {
32147                             targets.remove(this.dropZone);
32148                                 }
32149                     if (targets.length == 1) {
32150                                         this.dragZone.cachedTarget = null;
32151                         var el = Roo.get(targets[0].getEl());
32152                         var box = el.getBox(true);
32153                         targets[0].onNodeDrop(el.dom, {
32154                                 target: el.dom,
32155                                 xy: [box.x, box.y + box.height - 1]
32156                         }, null, this.getDragData(e));
32157                     }
32158                 }
32159         }
32160     },
32161     
32162     handleSelection: function(e) {
32163                 this.dragZone.cachedTarget = null;
32164         var item = this.findItemFromChild(e.getTarget());
32165         if (!item) {
32166                 this.clearSelections(true);
32167                 return;
32168         }
32169                 if (item && (this.multiSelect || this.singleSelect)){
32170                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32171                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32172                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32173                                 this.unselect(item);
32174                         } else {
32175                                 this.select(item, this.multiSelect && e.ctrlKey);
32176                                 this.lastSelection = item;
32177                         }
32178                 }
32179     },
32180
32181     onItemClick : function(item, index, e){
32182                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32183                         return false;
32184                 }
32185                 return true;
32186     },
32187
32188     unselect : function(nodeInfo, suppressEvent){
32189                 var node = this.getNode(nodeInfo);
32190                 if(node && this.isSelected(node)){
32191                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32192                                 Roo.fly(node).removeClass(this.selectedClass);
32193                                 this.selections.remove(node);
32194                                 if(!suppressEvent){
32195                                         this.fireEvent("selectionchange", this, this.selections);
32196                                 }
32197                         }
32198                 }
32199     }
32200 });
32201 /*
32202  * Based on:
32203  * Ext JS Library 1.1.1
32204  * Copyright(c) 2006-2007, Ext JS, LLC.
32205  *
32206  * Originally Released Under LGPL - original licence link has changed is not relivant.
32207  *
32208  * Fork - LGPL
32209  * <script type="text/javascript">
32210  */
32211  
32212 /**
32213  * @class Roo.LayoutManager
32214  * @extends Roo.util.Observable
32215  * Base class for layout managers.
32216  */
32217 Roo.LayoutManager = function(container, config){
32218     Roo.LayoutManager.superclass.constructor.call(this);
32219     this.el = Roo.get(container);
32220     // ie scrollbar fix
32221     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32222         document.body.scroll = "no";
32223     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32224         this.el.position('relative');
32225     }
32226     this.id = this.el.id;
32227     this.el.addClass("x-layout-container");
32228     /** false to disable window resize monitoring @type Boolean */
32229     this.monitorWindowResize = true;
32230     this.regions = {};
32231     this.addEvents({
32232         /**
32233          * @event layout
32234          * Fires when a layout is performed. 
32235          * @param {Roo.LayoutManager} this
32236          */
32237         "layout" : true,
32238         /**
32239          * @event regionresized
32240          * Fires when the user resizes a region. 
32241          * @param {Roo.LayoutRegion} region The resized region
32242          * @param {Number} newSize The new size (width for east/west, height for north/south)
32243          */
32244         "regionresized" : true,
32245         /**
32246          * @event regioncollapsed
32247          * Fires when a region is collapsed. 
32248          * @param {Roo.LayoutRegion} region The collapsed region
32249          */
32250         "regioncollapsed" : true,
32251         /**
32252          * @event regionexpanded
32253          * Fires when a region is expanded.  
32254          * @param {Roo.LayoutRegion} region The expanded region
32255          */
32256         "regionexpanded" : true
32257     });
32258     this.updating = false;
32259     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32260 };
32261
32262 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32263     /**
32264      * Returns true if this layout is currently being updated
32265      * @return {Boolean}
32266      */
32267     isUpdating : function(){
32268         return this.updating; 
32269     },
32270     
32271     /**
32272      * Suspend the LayoutManager from doing auto-layouts while
32273      * making multiple add or remove calls
32274      */
32275     beginUpdate : function(){
32276         this.updating = true;    
32277     },
32278     
32279     /**
32280      * Restore auto-layouts and optionally disable the manager from performing a layout
32281      * @param {Boolean} noLayout true to disable a layout update 
32282      */
32283     endUpdate : function(noLayout){
32284         this.updating = false;
32285         if(!noLayout){
32286             this.layout();
32287         }    
32288     },
32289     
32290     layout: function(){
32291         
32292     },
32293     
32294     onRegionResized : function(region, newSize){
32295         this.fireEvent("regionresized", region, newSize);
32296         this.layout();
32297     },
32298     
32299     onRegionCollapsed : function(region){
32300         this.fireEvent("regioncollapsed", region);
32301     },
32302     
32303     onRegionExpanded : function(region){
32304         this.fireEvent("regionexpanded", region);
32305     },
32306         
32307     /**
32308      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32309      * performs box-model adjustments.
32310      * @return {Object} The size as an object {width: (the width), height: (the height)}
32311      */
32312     getViewSize : function(){
32313         var size;
32314         if(this.el.dom != document.body){
32315             size = this.el.getSize();
32316         }else{
32317             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32318         }
32319         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32320         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32321         return size;
32322     },
32323     
32324     /**
32325      * Returns the Element this layout is bound to.
32326      * @return {Roo.Element}
32327      */
32328     getEl : function(){
32329         return this.el;
32330     },
32331     
32332     /**
32333      * Returns the specified region.
32334      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32335      * @return {Roo.LayoutRegion}
32336      */
32337     getRegion : function(target){
32338         return this.regions[target.toLowerCase()];
32339     },
32340     
32341     onWindowResize : function(){
32342         if(this.monitorWindowResize){
32343             this.layout();
32344         }
32345     }
32346 });/*
32347  * Based on:
32348  * Ext JS Library 1.1.1
32349  * Copyright(c) 2006-2007, Ext JS, LLC.
32350  *
32351  * Originally Released Under LGPL - original licence link has changed is not relivant.
32352  *
32353  * Fork - LGPL
32354  * <script type="text/javascript">
32355  */
32356 /**
32357  * @class Roo.BorderLayout
32358  * @extends Roo.LayoutManager
32359  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32360  * please see: <br><br>
32361  * <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>
32362  * <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>
32363  * Example:
32364  <pre><code>
32365  var layout = new Roo.BorderLayout(document.body, {
32366     north: {
32367         initialSize: 25,
32368         titlebar: false
32369     },
32370     west: {
32371         split:true,
32372         initialSize: 200,
32373         minSize: 175,
32374         maxSize: 400,
32375         titlebar: true,
32376         collapsible: true
32377     },
32378     east: {
32379         split:true,
32380         initialSize: 202,
32381         minSize: 175,
32382         maxSize: 400,
32383         titlebar: true,
32384         collapsible: true
32385     },
32386     south: {
32387         split:true,
32388         initialSize: 100,
32389         minSize: 100,
32390         maxSize: 200,
32391         titlebar: true,
32392         collapsible: true
32393     },
32394     center: {
32395         titlebar: true,
32396         autoScroll:true,
32397         resizeTabs: true,
32398         minTabWidth: 50,
32399         preferredTabWidth: 150
32400     }
32401 });
32402
32403 // shorthand
32404 var CP = Roo.ContentPanel;
32405
32406 layout.beginUpdate();
32407 layout.add("north", new CP("north", "North"));
32408 layout.add("south", new CP("south", {title: "South", closable: true}));
32409 layout.add("west", new CP("west", {title: "West"}));
32410 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32411 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32412 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32413 layout.getRegion("center").showPanel("center1");
32414 layout.endUpdate();
32415 </code></pre>
32416
32417 <b>The container the layout is rendered into can be either the body element or any other element.
32418 If it is not the body element, the container needs to either be an absolute positioned element,
32419 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32420 the container size if it is not the body element.</b>
32421
32422 * @constructor
32423 * Create a new BorderLayout
32424 * @param {String/HTMLElement/Element} container The container this layout is bound to
32425 * @param {Object} config Configuration options
32426  */
32427 Roo.BorderLayout = function(container, config){
32428     config = config || {};
32429     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32430     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32431     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32432         var target = this.factory.validRegions[i];
32433         if(config[target]){
32434             this.addRegion(target, config[target]);
32435         }
32436     }
32437 };
32438
32439 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32440     /**
32441      * Creates and adds a new region if it doesn't already exist.
32442      * @param {String} target The target region key (north, south, east, west or center).
32443      * @param {Object} config The regions config object
32444      * @return {BorderLayoutRegion} The new region
32445      */
32446     addRegion : function(target, config){
32447         if(!this.regions[target]){
32448             var r = this.factory.create(target, this, config);
32449             this.bindRegion(target, r);
32450         }
32451         return this.regions[target];
32452     },
32453
32454     // private (kinda)
32455     bindRegion : function(name, r){
32456         this.regions[name] = r;
32457         r.on("visibilitychange", this.layout, this);
32458         r.on("paneladded", this.layout, this);
32459         r.on("panelremoved", this.layout, this);
32460         r.on("invalidated", this.layout, this);
32461         r.on("resized", this.onRegionResized, this);
32462         r.on("collapsed", this.onRegionCollapsed, this);
32463         r.on("expanded", this.onRegionExpanded, this);
32464     },
32465
32466     /**
32467      * Performs a layout update.
32468      */
32469     layout : function(){
32470         if(this.updating) return;
32471         var size = this.getViewSize();
32472         var w = size.width;
32473         var h = size.height;
32474         var centerW = w;
32475         var centerH = h;
32476         var centerY = 0;
32477         var centerX = 0;
32478         //var x = 0, y = 0;
32479
32480         var rs = this.regions;
32481         var north = rs["north"];
32482         var south = rs["south"]; 
32483         var west = rs["west"];
32484         var east = rs["east"];
32485         var center = rs["center"];
32486         //if(this.hideOnLayout){ // not supported anymore
32487             //c.el.setStyle("display", "none");
32488         //}
32489         if(north && north.isVisible()){
32490             var b = north.getBox();
32491             var m = north.getMargins();
32492             b.width = w - (m.left+m.right);
32493             b.x = m.left;
32494             b.y = m.top;
32495             centerY = b.height + b.y + m.bottom;
32496             centerH -= centerY;
32497             north.updateBox(this.safeBox(b));
32498         }
32499         if(south && south.isVisible()){
32500             var b = south.getBox();
32501             var m = south.getMargins();
32502             b.width = w - (m.left+m.right);
32503             b.x = m.left;
32504             var totalHeight = (b.height + m.top + m.bottom);
32505             b.y = h - totalHeight + m.top;
32506             centerH -= totalHeight;
32507             south.updateBox(this.safeBox(b));
32508         }
32509         if(west && west.isVisible()){
32510             var b = west.getBox();
32511             var m = west.getMargins();
32512             b.height = centerH - (m.top+m.bottom);
32513             b.x = m.left;
32514             b.y = centerY + m.top;
32515             var totalWidth = (b.width + m.left + m.right);
32516             centerX += totalWidth;
32517             centerW -= totalWidth;
32518             west.updateBox(this.safeBox(b));
32519         }
32520         if(east && east.isVisible()){
32521             var b = east.getBox();
32522             var m = east.getMargins();
32523             b.height = centerH - (m.top+m.bottom);
32524             var totalWidth = (b.width + m.left + m.right);
32525             b.x = w - totalWidth + m.left;
32526             b.y = centerY + m.top;
32527             centerW -= totalWidth;
32528             east.updateBox(this.safeBox(b));
32529         }
32530         if(center){
32531             var m = center.getMargins();
32532             var centerBox = {
32533                 x: centerX + m.left,
32534                 y: centerY + m.top,
32535                 width: centerW - (m.left+m.right),
32536                 height: centerH - (m.top+m.bottom)
32537             };
32538             //if(this.hideOnLayout){
32539                 //center.el.setStyle("display", "block");
32540             //}
32541             center.updateBox(this.safeBox(centerBox));
32542         }
32543         this.el.repaint();
32544         this.fireEvent("layout", this);
32545     },
32546
32547     // private
32548     safeBox : function(box){
32549         box.width = Math.max(0, box.width);
32550         box.height = Math.max(0, box.height);
32551         return box;
32552     },
32553
32554     /**
32555      * Adds a ContentPanel (or subclass) to this layout.
32556      * @param {String} target The target region key (north, south, east, west or center).
32557      * @param {Roo.ContentPanel} panel The panel to add
32558      * @return {Roo.ContentPanel} The added panel
32559      */
32560     add : function(target, panel){
32561          
32562         target = target.toLowerCase();
32563         return this.regions[target].add(panel);
32564     },
32565
32566     /**
32567      * Remove a ContentPanel (or subclass) to this layout.
32568      * @param {String} target The target region key (north, south, east, west or center).
32569      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32570      * @return {Roo.ContentPanel} The removed panel
32571      */
32572     remove : function(target, panel){
32573         target = target.toLowerCase();
32574         return this.regions[target].remove(panel);
32575     },
32576
32577     /**
32578      * Searches all regions for a panel with the specified id
32579      * @param {String} panelId
32580      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32581      */
32582     findPanel : function(panelId){
32583         var rs = this.regions;
32584         for(var target in rs){
32585             if(typeof rs[target] != "function"){
32586                 var p = rs[target].getPanel(panelId);
32587                 if(p){
32588                     return p;
32589                 }
32590             }
32591         }
32592         return null;
32593     },
32594
32595     /**
32596      * Searches all regions for a panel with the specified id and activates (shows) it.
32597      * @param {String/ContentPanel} panelId The panels id or the panel itself
32598      * @return {Roo.ContentPanel} The shown panel or null
32599      */
32600     showPanel : function(panelId) {
32601       var rs = this.regions;
32602       for(var target in rs){
32603          var r = rs[target];
32604          if(typeof r != "function"){
32605             if(r.hasPanel(panelId)){
32606                return r.showPanel(panelId);
32607             }
32608          }
32609       }
32610       return null;
32611    },
32612
32613    /**
32614      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32615      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32616      */
32617     restoreState : function(provider){
32618         if(!provider){
32619             provider = Roo.state.Manager;
32620         }
32621         var sm = new Roo.LayoutStateManager();
32622         sm.init(this, provider);
32623     },
32624
32625     /**
32626      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32627      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32628      * a valid ContentPanel config object.  Example:
32629      * <pre><code>
32630 // Create the main layout
32631 var layout = new Roo.BorderLayout('main-ct', {
32632     west: {
32633         split:true,
32634         minSize: 175,
32635         titlebar: true
32636     },
32637     center: {
32638         title:'Components'
32639     }
32640 }, 'main-ct');
32641
32642 // Create and add multiple ContentPanels at once via configs
32643 layout.batchAdd({
32644    west: {
32645        id: 'source-files',
32646        autoCreate:true,
32647        title:'Ext Source Files',
32648        autoScroll:true,
32649        fitToFrame:true
32650    },
32651    center : {
32652        el: cview,
32653        autoScroll:true,
32654        fitToFrame:true,
32655        toolbar: tb,
32656        resizeEl:'cbody'
32657    }
32658 });
32659 </code></pre>
32660      * @param {Object} regions An object containing ContentPanel configs by region name
32661      */
32662     batchAdd : function(regions){
32663         this.beginUpdate();
32664         for(var rname in regions){
32665             var lr = this.regions[rname];
32666             if(lr){
32667                 this.addTypedPanels(lr, regions[rname]);
32668             }
32669         }
32670         this.endUpdate();
32671     },
32672
32673     // private
32674     addTypedPanels : function(lr, ps){
32675         if(typeof ps == 'string'){
32676             lr.add(new Roo.ContentPanel(ps));
32677         }
32678         else if(ps instanceof Array){
32679             for(var i =0, len = ps.length; i < len; i++){
32680                 this.addTypedPanels(lr, ps[i]);
32681             }
32682         }
32683         else if(!ps.events){ // raw config?
32684             var el = ps.el;
32685             delete ps.el; // prevent conflict
32686             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32687         }
32688         else {  // panel object assumed!
32689             lr.add(ps);
32690         }
32691     },
32692     /**
32693      * Adds a xtype elements to the layout.
32694      * <pre><code>
32695
32696 layout.addxtype({
32697        xtype : 'ContentPanel',
32698        region: 'west',
32699        items: [ .... ]
32700    }
32701 );
32702
32703 layout.addxtype({
32704         xtype : 'NestedLayoutPanel',
32705         region: 'west',
32706         layout: {
32707            center: { },
32708            west: { }   
32709         },
32710         items : [ ... list of content panels or nested layout panels.. ]
32711    }
32712 );
32713 </code></pre>
32714      * @param {Object} cfg Xtype definition of item to add.
32715      */
32716     addxtype : function(cfg)
32717     {
32718         // basically accepts a pannel...
32719         // can accept a layout region..!?!?
32720         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32721         
32722         if (!cfg.xtype.match(/Panel$/)) {
32723             return false;
32724         }
32725         var ret = false;
32726         
32727         if (typeof(cfg.region) == 'undefined') {
32728             Roo.log("Failed to add Panel, region was not set");
32729             Roo.log(cfg);
32730             return false;
32731         }
32732         var region = cfg.region;
32733         delete cfg.region;
32734         
32735           
32736         var xitems = [];
32737         if (cfg.items) {
32738             xitems = cfg.items;
32739             delete cfg.items;
32740         }
32741         var nb = false;
32742         
32743         switch(cfg.xtype) 
32744         {
32745             case 'ContentPanel':  // ContentPanel (el, cfg)
32746             case 'ScrollPanel':  // ContentPanel (el, cfg)
32747             case 'ViewPanel': 
32748                 if(cfg.autoCreate) {
32749                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32750                 } else {
32751                     var el = this.el.createChild();
32752                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32753                 }
32754                 
32755                 this.add(region, ret);
32756                 break;
32757             
32758             
32759             case 'TreePanel': // our new panel!
32760                 cfg.el = this.el.createChild();
32761                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32762                 this.add(region, ret);
32763                 break;
32764             
32765             case 'NestedLayoutPanel': 
32766                 // create a new Layout (which is  a Border Layout...
32767                 var el = this.el.createChild();
32768                 var clayout = cfg.layout;
32769                 delete cfg.layout;
32770                 clayout.items   = clayout.items  || [];
32771                 // replace this exitems with the clayout ones..
32772                 xitems = clayout.items;
32773                  
32774                 
32775                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32776                     cfg.background = false;
32777                 }
32778                 var layout = new Roo.BorderLayout(el, clayout);
32779                 
32780                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32781                 //console.log('adding nested layout panel '  + cfg.toSource());
32782                 this.add(region, ret);
32783                 nb = {}; /// find first...
32784                 break;
32785                 
32786             case 'GridPanel': 
32787             
32788                 // needs grid and region
32789                 
32790                 //var el = this.getRegion(region).el.createChild();
32791                 var el = this.el.createChild();
32792                 // create the grid first...
32793                 
32794                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32795                 delete cfg.grid;
32796                 if (region == 'center' && this.active ) {
32797                     cfg.background = false;
32798                 }
32799                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32800                 
32801                 this.add(region, ret);
32802                 if (cfg.background) {
32803                     ret.on('activate', function(gp) {
32804                         if (!gp.grid.rendered) {
32805                             gp.grid.render();
32806                         }
32807                     });
32808                 } else {
32809                     grid.render();
32810                 }
32811                 break;
32812            
32813            
32814            
32815                 
32816                 
32817                 
32818             default: 
32819                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32820                 return null;
32821              // GridPanel (grid, cfg)
32822             
32823         }
32824         this.beginUpdate();
32825         // add children..
32826         var region = '';
32827         var abn = {};
32828         Roo.each(xitems, function(i)  {
32829             region = nb && i.region ? i.region : false;
32830             
32831             var add = ret.addxtype(i);
32832            
32833             if (region) {
32834                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32835                 if (!i.background) {
32836                     abn[region] = nb[region] ;
32837                 }
32838             }
32839             
32840         });
32841         this.endUpdate();
32842
32843         // make the last non-background panel active..
32844         //if (nb) { Roo.log(abn); }
32845         if (nb) {
32846             
32847             for(var r in abn) {
32848                 region = this.getRegion(r);
32849                 if (region) {
32850                     // tried using nb[r], but it does not work..
32851                      
32852                     region.showPanel(abn[r]);
32853                    
32854                 }
32855             }
32856         }
32857         return ret;
32858         
32859     }
32860 });
32861
32862 /**
32863  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32864  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32865  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32866  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32867  * <pre><code>
32868 // shorthand
32869 var CP = Roo.ContentPanel;
32870
32871 var layout = Roo.BorderLayout.create({
32872     north: {
32873         initialSize: 25,
32874         titlebar: false,
32875         panels: [new CP("north", "North")]
32876     },
32877     west: {
32878         split:true,
32879         initialSize: 200,
32880         minSize: 175,
32881         maxSize: 400,
32882         titlebar: true,
32883         collapsible: true,
32884         panels: [new CP("west", {title: "West"})]
32885     },
32886     east: {
32887         split:true,
32888         initialSize: 202,
32889         minSize: 175,
32890         maxSize: 400,
32891         titlebar: true,
32892         collapsible: true,
32893         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32894     },
32895     south: {
32896         split:true,
32897         initialSize: 100,
32898         minSize: 100,
32899         maxSize: 200,
32900         titlebar: true,
32901         collapsible: true,
32902         panels: [new CP("south", {title: "South", closable: true})]
32903     },
32904     center: {
32905         titlebar: true,
32906         autoScroll:true,
32907         resizeTabs: true,
32908         minTabWidth: 50,
32909         preferredTabWidth: 150,
32910         panels: [
32911             new CP("center1", {title: "Close Me", closable: true}),
32912             new CP("center2", {title: "Center Panel", closable: false})
32913         ]
32914     }
32915 }, document.body);
32916
32917 layout.getRegion("center").showPanel("center1");
32918 </code></pre>
32919  * @param config
32920  * @param targetEl
32921  */
32922 Roo.BorderLayout.create = function(config, targetEl){
32923     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32924     layout.beginUpdate();
32925     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32926     for(var j = 0, jlen = regions.length; j < jlen; j++){
32927         var lr = regions[j];
32928         if(layout.regions[lr] && config[lr].panels){
32929             var r = layout.regions[lr];
32930             var ps = config[lr].panels;
32931             layout.addTypedPanels(r, ps);
32932         }
32933     }
32934     layout.endUpdate();
32935     return layout;
32936 };
32937
32938 // private
32939 Roo.BorderLayout.RegionFactory = {
32940     // private
32941     validRegions : ["north","south","east","west","center"],
32942
32943     // private
32944     create : function(target, mgr, config){
32945         target = target.toLowerCase();
32946         if(config.lightweight || config.basic){
32947             return new Roo.BasicLayoutRegion(mgr, config, target);
32948         }
32949         switch(target){
32950             case "north":
32951                 return new Roo.NorthLayoutRegion(mgr, config);
32952             case "south":
32953                 return new Roo.SouthLayoutRegion(mgr, config);
32954             case "east":
32955                 return new Roo.EastLayoutRegion(mgr, config);
32956             case "west":
32957                 return new Roo.WestLayoutRegion(mgr, config);
32958             case "center":
32959                 return new Roo.CenterLayoutRegion(mgr, config);
32960         }
32961         throw 'Layout region "'+target+'" not supported.';
32962     }
32963 };/*
32964  * Based on:
32965  * Ext JS Library 1.1.1
32966  * Copyright(c) 2006-2007, Ext JS, LLC.
32967  *
32968  * Originally Released Under LGPL - original licence link has changed is not relivant.
32969  *
32970  * Fork - LGPL
32971  * <script type="text/javascript">
32972  */
32973  
32974 /**
32975  * @class Roo.BasicLayoutRegion
32976  * @extends Roo.util.Observable
32977  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32978  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32979  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32980  */
32981 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32982     this.mgr = mgr;
32983     this.position  = pos;
32984     this.events = {
32985         /**
32986          * @scope Roo.BasicLayoutRegion
32987          */
32988         
32989         /**
32990          * @event beforeremove
32991          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32992          * @param {Roo.LayoutRegion} this
32993          * @param {Roo.ContentPanel} panel The panel
32994          * @param {Object} e The cancel event object
32995          */
32996         "beforeremove" : true,
32997         /**
32998          * @event invalidated
32999          * Fires when the layout for this region is changed.
33000          * @param {Roo.LayoutRegion} this
33001          */
33002         "invalidated" : true,
33003         /**
33004          * @event visibilitychange
33005          * Fires when this region is shown or hidden 
33006          * @param {Roo.LayoutRegion} this
33007          * @param {Boolean} visibility true or false
33008          */
33009         "visibilitychange" : true,
33010         /**
33011          * @event paneladded
33012          * Fires when a panel is added. 
33013          * @param {Roo.LayoutRegion} this
33014          * @param {Roo.ContentPanel} panel The panel
33015          */
33016         "paneladded" : true,
33017         /**
33018          * @event panelremoved
33019          * Fires when a panel is removed. 
33020          * @param {Roo.LayoutRegion} this
33021          * @param {Roo.ContentPanel} panel The panel
33022          */
33023         "panelremoved" : true,
33024         /**
33025          * @event collapsed
33026          * Fires when this region is collapsed.
33027          * @param {Roo.LayoutRegion} this
33028          */
33029         "collapsed" : true,
33030         /**
33031          * @event expanded
33032          * Fires when this region is expanded.
33033          * @param {Roo.LayoutRegion} this
33034          */
33035         "expanded" : true,
33036         /**
33037          * @event slideshow
33038          * Fires when this region is slid into view.
33039          * @param {Roo.LayoutRegion} this
33040          */
33041         "slideshow" : true,
33042         /**
33043          * @event slidehide
33044          * Fires when this region slides out of view. 
33045          * @param {Roo.LayoutRegion} this
33046          */
33047         "slidehide" : true,
33048         /**
33049          * @event panelactivated
33050          * Fires when a panel is activated. 
33051          * @param {Roo.LayoutRegion} this
33052          * @param {Roo.ContentPanel} panel The activated panel
33053          */
33054         "panelactivated" : true,
33055         /**
33056          * @event resized
33057          * Fires when the user resizes this region. 
33058          * @param {Roo.LayoutRegion} this
33059          * @param {Number} newSize The new size (width for east/west, height for north/south)
33060          */
33061         "resized" : true
33062     };
33063     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33064     this.panels = new Roo.util.MixedCollection();
33065     this.panels.getKey = this.getPanelId.createDelegate(this);
33066     this.box = null;
33067     this.activePanel = null;
33068     // ensure listeners are added...
33069     
33070     if (config.listeners || config.events) {
33071         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33072             listeners : config.listeners || {},
33073             events : config.events || {}
33074         });
33075     }
33076     
33077     if(skipConfig !== true){
33078         this.applyConfig(config);
33079     }
33080 };
33081
33082 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33083     getPanelId : function(p){
33084         return p.getId();
33085     },
33086     
33087     applyConfig : function(config){
33088         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33089         this.config = config;
33090         
33091     },
33092     
33093     /**
33094      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33095      * the width, for horizontal (north, south) the height.
33096      * @param {Number} newSize The new width or height
33097      */
33098     resizeTo : function(newSize){
33099         var el = this.el ? this.el :
33100                  (this.activePanel ? this.activePanel.getEl() : null);
33101         if(el){
33102             switch(this.position){
33103                 case "east":
33104                 case "west":
33105                     el.setWidth(newSize);
33106                     this.fireEvent("resized", this, newSize);
33107                 break;
33108                 case "north":
33109                 case "south":
33110                     el.setHeight(newSize);
33111                     this.fireEvent("resized", this, newSize);
33112                 break;                
33113             }
33114         }
33115     },
33116     
33117     getBox : function(){
33118         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33119     },
33120     
33121     getMargins : function(){
33122         return this.margins;
33123     },
33124     
33125     updateBox : function(box){
33126         this.box = box;
33127         var el = this.activePanel.getEl();
33128         el.dom.style.left = box.x + "px";
33129         el.dom.style.top = box.y + "px";
33130         this.activePanel.setSize(box.width, box.height);
33131     },
33132     
33133     /**
33134      * Returns the container element for this region.
33135      * @return {Roo.Element}
33136      */
33137     getEl : function(){
33138         return this.activePanel;
33139     },
33140     
33141     /**
33142      * Returns true if this region is currently visible.
33143      * @return {Boolean}
33144      */
33145     isVisible : function(){
33146         return this.activePanel ? true : false;
33147     },
33148     
33149     setActivePanel : function(panel){
33150         panel = this.getPanel(panel);
33151         if(this.activePanel && this.activePanel != panel){
33152             this.activePanel.setActiveState(false);
33153             this.activePanel.getEl().setLeftTop(-10000,-10000);
33154         }
33155         this.activePanel = panel;
33156         panel.setActiveState(true);
33157         if(this.box){
33158             panel.setSize(this.box.width, this.box.height);
33159         }
33160         this.fireEvent("panelactivated", this, panel);
33161         this.fireEvent("invalidated");
33162     },
33163     
33164     /**
33165      * Show the specified panel.
33166      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33167      * @return {Roo.ContentPanel} The shown panel or null
33168      */
33169     showPanel : function(panel){
33170         if(panel = this.getPanel(panel)){
33171             this.setActivePanel(panel);
33172         }
33173         return panel;
33174     },
33175     
33176     /**
33177      * Get the active panel for this region.
33178      * @return {Roo.ContentPanel} The active panel or null
33179      */
33180     getActivePanel : function(){
33181         return this.activePanel;
33182     },
33183     
33184     /**
33185      * Add the passed ContentPanel(s)
33186      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33187      * @return {Roo.ContentPanel} The panel added (if only one was added)
33188      */
33189     add : function(panel){
33190         if(arguments.length > 1){
33191             for(var i = 0, len = arguments.length; i < len; i++) {
33192                 this.add(arguments[i]);
33193             }
33194             return null;
33195         }
33196         if(this.hasPanel(panel)){
33197             this.showPanel(panel);
33198             return panel;
33199         }
33200         var el = panel.getEl();
33201         if(el.dom.parentNode != this.mgr.el.dom){
33202             this.mgr.el.dom.appendChild(el.dom);
33203         }
33204         if(panel.setRegion){
33205             panel.setRegion(this);
33206         }
33207         this.panels.add(panel);
33208         el.setStyle("position", "absolute");
33209         if(!panel.background){
33210             this.setActivePanel(panel);
33211             if(this.config.initialSize && this.panels.getCount()==1){
33212                 this.resizeTo(this.config.initialSize);
33213             }
33214         }
33215         this.fireEvent("paneladded", this, panel);
33216         return panel;
33217     },
33218     
33219     /**
33220      * Returns true if the panel is in this region.
33221      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33222      * @return {Boolean}
33223      */
33224     hasPanel : function(panel){
33225         if(typeof panel == "object"){ // must be panel obj
33226             panel = panel.getId();
33227         }
33228         return this.getPanel(panel) ? true : false;
33229     },
33230     
33231     /**
33232      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33233      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33234      * @param {Boolean} preservePanel Overrides the config preservePanel option
33235      * @return {Roo.ContentPanel} The panel that was removed
33236      */
33237     remove : function(panel, preservePanel){
33238         panel = this.getPanel(panel);
33239         if(!panel){
33240             return null;
33241         }
33242         var e = {};
33243         this.fireEvent("beforeremove", this, panel, e);
33244         if(e.cancel === true){
33245             return null;
33246         }
33247         var panelId = panel.getId();
33248         this.panels.removeKey(panelId);
33249         return panel;
33250     },
33251     
33252     /**
33253      * Returns the panel specified or null if it's not in this region.
33254      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33255      * @return {Roo.ContentPanel}
33256      */
33257     getPanel : function(id){
33258         if(typeof id == "object"){ // must be panel obj
33259             return id;
33260         }
33261         return this.panels.get(id);
33262     },
33263     
33264     /**
33265      * Returns this regions position (north/south/east/west/center).
33266      * @return {String} 
33267      */
33268     getPosition: function(){
33269         return this.position;    
33270     }
33271 });/*
33272  * Based on:
33273  * Ext JS Library 1.1.1
33274  * Copyright(c) 2006-2007, Ext JS, LLC.
33275  *
33276  * Originally Released Under LGPL - original licence link has changed is not relivant.
33277  *
33278  * Fork - LGPL
33279  * <script type="text/javascript">
33280  */
33281  
33282 /**
33283  * @class Roo.LayoutRegion
33284  * @extends Roo.BasicLayoutRegion
33285  * This class represents a region in a layout manager.
33286  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33287  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33288  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33289  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33290  * @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})
33291  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33292  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33293  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33294  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33295  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33296  * @cfg {String}    title           The title for the region (overrides panel titles)
33297  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33298  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33299  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33300  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33301  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33302  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33303  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33304  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33305  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33306  * @cfg {Boolean}   showPin         True to show a pin button
33307  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33308  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33309  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33310  * @cfg {Number}    width           For East/West panels
33311  * @cfg {Number}    height          For North/South panels
33312  * @cfg {Boolean}   split           To show the splitter
33313  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33314  */
33315 Roo.LayoutRegion = function(mgr, config, pos){
33316     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33317     var dh = Roo.DomHelper;
33318     /** This region's container element 
33319     * @type Roo.Element */
33320     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33321     /** This region's title element 
33322     * @type Roo.Element */
33323
33324     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33325         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33326         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33327     ]}, true);
33328     this.titleEl.enableDisplayMode();
33329     /** This region's title text element 
33330     * @type HTMLElement */
33331     this.titleTextEl = this.titleEl.dom.firstChild;
33332     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33333     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33334     this.closeBtn.enableDisplayMode();
33335     this.closeBtn.on("click", this.closeClicked, this);
33336     this.closeBtn.hide();
33337
33338     this.createBody(config);
33339     this.visible = true;
33340     this.collapsed = false;
33341
33342     if(config.hideWhenEmpty){
33343         this.hide();
33344         this.on("paneladded", this.validateVisibility, this);
33345         this.on("panelremoved", this.validateVisibility, this);
33346     }
33347     this.applyConfig(config);
33348 };
33349
33350 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33351
33352     createBody : function(){
33353         /** This region's body element 
33354         * @type Roo.Element */
33355         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33356     },
33357
33358     applyConfig : function(c){
33359         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33360             var dh = Roo.DomHelper;
33361             if(c.titlebar !== false){
33362                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33363                 this.collapseBtn.on("click", this.collapse, this);
33364                 this.collapseBtn.enableDisplayMode();
33365
33366                 if(c.showPin === true || this.showPin){
33367                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33368                     this.stickBtn.enableDisplayMode();
33369                     this.stickBtn.on("click", this.expand, this);
33370                     this.stickBtn.hide();
33371                 }
33372             }
33373             /** This region's collapsed element
33374             * @type Roo.Element */
33375             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33376                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33377             ]}, true);
33378             if(c.floatable !== false){
33379                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33380                this.collapsedEl.on("click", this.collapseClick, this);
33381             }
33382
33383             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33384                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33385                    id: "message", unselectable: "on", style:{"float":"left"}});
33386                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33387              }
33388             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33389             this.expandBtn.on("click", this.expand, this);
33390         }
33391         if(this.collapseBtn){
33392             this.collapseBtn.setVisible(c.collapsible == true);
33393         }
33394         this.cmargins = c.cmargins || this.cmargins ||
33395                          (this.position == "west" || this.position == "east" ?
33396                              {top: 0, left: 2, right:2, bottom: 0} :
33397                              {top: 2, left: 0, right:0, bottom: 2});
33398         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33399         this.bottomTabs = c.tabPosition != "top";
33400         this.autoScroll = c.autoScroll || false;
33401         if(this.autoScroll){
33402             this.bodyEl.setStyle("overflow", "auto");
33403         }else{
33404             this.bodyEl.setStyle("overflow", "hidden");
33405         }
33406         //if(c.titlebar !== false){
33407             if((!c.titlebar && !c.title) || c.titlebar === false){
33408                 this.titleEl.hide();
33409             }else{
33410                 this.titleEl.show();
33411                 if(c.title){
33412                     this.titleTextEl.innerHTML = c.title;
33413                 }
33414             }
33415         //}
33416         this.duration = c.duration || .30;
33417         this.slideDuration = c.slideDuration || .45;
33418         this.config = c;
33419         if(c.collapsed){
33420             this.collapse(true);
33421         }
33422         if(c.hidden){
33423             this.hide();
33424         }
33425     },
33426     /**
33427      * Returns true if this region is currently visible.
33428      * @return {Boolean}
33429      */
33430     isVisible : function(){
33431         return this.visible;
33432     },
33433
33434     /**
33435      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33436      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33437      */
33438     setCollapsedTitle : function(title){
33439         title = title || "&#160;";
33440         if(this.collapsedTitleTextEl){
33441             this.collapsedTitleTextEl.innerHTML = title;
33442         }
33443     },
33444
33445     getBox : function(){
33446         var b;
33447         if(!this.collapsed){
33448             b = this.el.getBox(false, true);
33449         }else{
33450             b = this.collapsedEl.getBox(false, true);
33451         }
33452         return b;
33453     },
33454
33455     getMargins : function(){
33456         return this.collapsed ? this.cmargins : this.margins;
33457     },
33458
33459     highlight : function(){
33460         this.el.addClass("x-layout-panel-dragover");
33461     },
33462
33463     unhighlight : function(){
33464         this.el.removeClass("x-layout-panel-dragover");
33465     },
33466
33467     updateBox : function(box){
33468         this.box = box;
33469         if(!this.collapsed){
33470             this.el.dom.style.left = box.x + "px";
33471             this.el.dom.style.top = box.y + "px";
33472             this.updateBody(box.width, box.height);
33473         }else{
33474             this.collapsedEl.dom.style.left = box.x + "px";
33475             this.collapsedEl.dom.style.top = box.y + "px";
33476             this.collapsedEl.setSize(box.width, box.height);
33477         }
33478         if(this.tabs){
33479             this.tabs.autoSizeTabs();
33480         }
33481     },
33482
33483     updateBody : function(w, h){
33484         if(w !== null){
33485             this.el.setWidth(w);
33486             w -= this.el.getBorderWidth("rl");
33487             if(this.config.adjustments){
33488                 w += this.config.adjustments[0];
33489             }
33490         }
33491         if(h !== null){
33492             this.el.setHeight(h);
33493             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33494             h -= this.el.getBorderWidth("tb");
33495             if(this.config.adjustments){
33496                 h += this.config.adjustments[1];
33497             }
33498             this.bodyEl.setHeight(h);
33499             if(this.tabs){
33500                 h = this.tabs.syncHeight(h);
33501             }
33502         }
33503         if(this.panelSize){
33504             w = w !== null ? w : this.panelSize.width;
33505             h = h !== null ? h : this.panelSize.height;
33506         }
33507         if(this.activePanel){
33508             var el = this.activePanel.getEl();
33509             w = w !== null ? w : el.getWidth();
33510             h = h !== null ? h : el.getHeight();
33511             this.panelSize = {width: w, height: h};
33512             this.activePanel.setSize(w, h);
33513         }
33514         if(Roo.isIE && this.tabs){
33515             this.tabs.el.repaint();
33516         }
33517     },
33518
33519     /**
33520      * Returns the container element for this region.
33521      * @return {Roo.Element}
33522      */
33523     getEl : function(){
33524         return this.el;
33525     },
33526
33527     /**
33528      * Hides this region.
33529      */
33530     hide : function(){
33531         if(!this.collapsed){
33532             this.el.dom.style.left = "-2000px";
33533             this.el.hide();
33534         }else{
33535             this.collapsedEl.dom.style.left = "-2000px";
33536             this.collapsedEl.hide();
33537         }
33538         this.visible = false;
33539         this.fireEvent("visibilitychange", this, false);
33540     },
33541
33542     /**
33543      * Shows this region if it was previously hidden.
33544      */
33545     show : function(){
33546         if(!this.collapsed){
33547             this.el.show();
33548         }else{
33549             this.collapsedEl.show();
33550         }
33551         this.visible = true;
33552         this.fireEvent("visibilitychange", this, true);
33553     },
33554
33555     closeClicked : function(){
33556         if(this.activePanel){
33557             this.remove(this.activePanel);
33558         }
33559     },
33560
33561     collapseClick : function(e){
33562         if(this.isSlid){
33563            e.stopPropagation();
33564            this.slideIn();
33565         }else{
33566            e.stopPropagation();
33567            this.slideOut();
33568         }
33569     },
33570
33571     /**
33572      * Collapses this region.
33573      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33574      */
33575     collapse : function(skipAnim){
33576         if(this.collapsed) return;
33577         this.collapsed = true;
33578         if(this.split){
33579             this.split.el.hide();
33580         }
33581         if(this.config.animate && skipAnim !== true){
33582             this.fireEvent("invalidated", this);
33583             this.animateCollapse();
33584         }else{
33585             this.el.setLocation(-20000,-20000);
33586             this.el.hide();
33587             this.collapsedEl.show();
33588             this.fireEvent("collapsed", this);
33589             this.fireEvent("invalidated", this);
33590         }
33591     },
33592
33593     animateCollapse : function(){
33594         // overridden
33595     },
33596
33597     /**
33598      * Expands this region if it was previously collapsed.
33599      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33600      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33601      */
33602     expand : function(e, skipAnim){
33603         if(e) e.stopPropagation();
33604         if(!this.collapsed || this.el.hasActiveFx()) return;
33605         if(this.isSlid){
33606             this.afterSlideIn();
33607             skipAnim = true;
33608         }
33609         this.collapsed = false;
33610         if(this.config.animate && skipAnim !== true){
33611             this.animateExpand();
33612         }else{
33613             this.el.show();
33614             if(this.split){
33615                 this.split.el.show();
33616             }
33617             this.collapsedEl.setLocation(-2000,-2000);
33618             this.collapsedEl.hide();
33619             this.fireEvent("invalidated", this);
33620             this.fireEvent("expanded", this);
33621         }
33622     },
33623
33624     animateExpand : function(){
33625         // overridden
33626     },
33627
33628     initTabs : function()
33629     {
33630         this.bodyEl.setStyle("overflow", "hidden");
33631         var ts = new Roo.TabPanel(
33632                 this.bodyEl.dom,
33633                 {
33634                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33635                     disableTooltips: this.config.disableTabTips,
33636                     toolbar : this.config.toolbar
33637                 }
33638         );
33639         if(this.config.hideTabs){
33640             ts.stripWrap.setDisplayed(false);
33641         }
33642         this.tabs = ts;
33643         ts.resizeTabs = this.config.resizeTabs === true;
33644         ts.minTabWidth = this.config.minTabWidth || 40;
33645         ts.maxTabWidth = this.config.maxTabWidth || 250;
33646         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33647         ts.monitorResize = false;
33648         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33649         ts.bodyEl.addClass('x-layout-tabs-body');
33650         this.panels.each(this.initPanelAsTab, this);
33651     },
33652
33653     initPanelAsTab : function(panel){
33654         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33655                     this.config.closeOnTab && panel.isClosable());
33656         if(panel.tabTip !== undefined){
33657             ti.setTooltip(panel.tabTip);
33658         }
33659         ti.on("activate", function(){
33660               this.setActivePanel(panel);
33661         }, this);
33662         if(this.config.closeOnTab){
33663             ti.on("beforeclose", function(t, e){
33664                 e.cancel = true;
33665                 this.remove(panel);
33666             }, this);
33667         }
33668         return ti;
33669     },
33670
33671     updatePanelTitle : function(panel, title){
33672         if(this.activePanel == panel){
33673             this.updateTitle(title);
33674         }
33675         if(this.tabs){
33676             var ti = this.tabs.getTab(panel.getEl().id);
33677             ti.setText(title);
33678             if(panel.tabTip !== undefined){
33679                 ti.setTooltip(panel.tabTip);
33680             }
33681         }
33682     },
33683
33684     updateTitle : function(title){
33685         if(this.titleTextEl && !this.config.title){
33686             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33687         }
33688     },
33689
33690     setActivePanel : function(panel){
33691         panel = this.getPanel(panel);
33692         if(this.activePanel && this.activePanel != panel){
33693             this.activePanel.setActiveState(false);
33694         }
33695         this.activePanel = panel;
33696         panel.setActiveState(true);
33697         if(this.panelSize){
33698             panel.setSize(this.panelSize.width, this.panelSize.height);
33699         }
33700         if(this.closeBtn){
33701             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33702         }
33703         this.updateTitle(panel.getTitle());
33704         if(this.tabs){
33705             this.fireEvent("invalidated", this);
33706         }
33707         this.fireEvent("panelactivated", this, panel);
33708     },
33709
33710     /**
33711      * Shows the specified panel.
33712      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33713      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33714      */
33715     showPanel : function(panel){
33716         if(panel = this.getPanel(panel)){
33717             if(this.tabs){
33718                 var tab = this.tabs.getTab(panel.getEl().id);
33719                 if(tab.isHidden()){
33720                     this.tabs.unhideTab(tab.id);
33721                 }
33722                 tab.activate();
33723             }else{
33724                 this.setActivePanel(panel);
33725             }
33726         }
33727         return panel;
33728     },
33729
33730     /**
33731      * Get the active panel for this region.
33732      * @return {Roo.ContentPanel} The active panel or null
33733      */
33734     getActivePanel : function(){
33735         return this.activePanel;
33736     },
33737
33738     validateVisibility : function(){
33739         if(this.panels.getCount() < 1){
33740             this.updateTitle("&#160;");
33741             this.closeBtn.hide();
33742             this.hide();
33743         }else{
33744             if(!this.isVisible()){
33745                 this.show();
33746             }
33747         }
33748     },
33749
33750     /**
33751      * Adds the passed ContentPanel(s) to this region.
33752      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33753      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33754      */
33755     add : function(panel){
33756         if(arguments.length > 1){
33757             for(var i = 0, len = arguments.length; i < len; i++) {
33758                 this.add(arguments[i]);
33759             }
33760             return null;
33761         }
33762         if(this.hasPanel(panel)){
33763             this.showPanel(panel);
33764             return panel;
33765         }
33766         panel.setRegion(this);
33767         this.panels.add(panel);
33768         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33769             this.bodyEl.dom.appendChild(panel.getEl().dom);
33770             if(panel.background !== true){
33771                 this.setActivePanel(panel);
33772             }
33773             this.fireEvent("paneladded", this, panel);
33774             return panel;
33775         }
33776         if(!this.tabs){
33777             this.initTabs();
33778         }else{
33779             this.initPanelAsTab(panel);
33780         }
33781         if(panel.background !== true){
33782             this.tabs.activate(panel.getEl().id);
33783         }
33784         this.fireEvent("paneladded", this, panel);
33785         return panel;
33786     },
33787
33788     /**
33789      * Hides the tab for the specified panel.
33790      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33791      */
33792     hidePanel : function(panel){
33793         if(this.tabs && (panel = this.getPanel(panel))){
33794             this.tabs.hideTab(panel.getEl().id);
33795         }
33796     },
33797
33798     /**
33799      * Unhides the tab for a previously hidden panel.
33800      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33801      */
33802     unhidePanel : function(panel){
33803         if(this.tabs && (panel = this.getPanel(panel))){
33804             this.tabs.unhideTab(panel.getEl().id);
33805         }
33806     },
33807
33808     clearPanels : function(){
33809         while(this.panels.getCount() > 0){
33810              this.remove(this.panels.first());
33811         }
33812     },
33813
33814     /**
33815      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33816      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33817      * @param {Boolean} preservePanel Overrides the config preservePanel option
33818      * @return {Roo.ContentPanel} The panel that was removed
33819      */
33820     remove : function(panel, preservePanel){
33821         panel = this.getPanel(panel);
33822         if(!panel){
33823             return null;
33824         }
33825         var e = {};
33826         this.fireEvent("beforeremove", this, panel, e);
33827         if(e.cancel === true){
33828             return null;
33829         }
33830         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33831         var panelId = panel.getId();
33832         this.panels.removeKey(panelId);
33833         if(preservePanel){
33834             document.body.appendChild(panel.getEl().dom);
33835         }
33836         if(this.tabs){
33837             this.tabs.removeTab(panel.getEl().id);
33838         }else if (!preservePanel){
33839             this.bodyEl.dom.removeChild(panel.getEl().dom);
33840         }
33841         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33842             var p = this.panels.first();
33843             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33844             tempEl.appendChild(p.getEl().dom);
33845             this.bodyEl.update("");
33846             this.bodyEl.dom.appendChild(p.getEl().dom);
33847             tempEl = null;
33848             this.updateTitle(p.getTitle());
33849             this.tabs = null;
33850             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33851             this.setActivePanel(p);
33852         }
33853         panel.setRegion(null);
33854         if(this.activePanel == panel){
33855             this.activePanel = null;
33856         }
33857         if(this.config.autoDestroy !== false && preservePanel !== true){
33858             try{panel.destroy();}catch(e){}
33859         }
33860         this.fireEvent("panelremoved", this, panel);
33861         return panel;
33862     },
33863
33864     /**
33865      * Returns the TabPanel component used by this region
33866      * @return {Roo.TabPanel}
33867      */
33868     getTabs : function(){
33869         return this.tabs;
33870     },
33871
33872     createTool : function(parentEl, className){
33873         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33874             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33875         btn.addClassOnOver("x-layout-tools-button-over");
33876         return btn;
33877     }
33878 });/*
33879  * Based on:
33880  * Ext JS Library 1.1.1
33881  * Copyright(c) 2006-2007, Ext JS, LLC.
33882  *
33883  * Originally Released Under LGPL - original licence link has changed is not relivant.
33884  *
33885  * Fork - LGPL
33886  * <script type="text/javascript">
33887  */
33888  
33889
33890
33891 /**
33892  * @class Roo.SplitLayoutRegion
33893  * @extends Roo.LayoutRegion
33894  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33895  */
33896 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33897     this.cursor = cursor;
33898     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33899 };
33900
33901 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33902     splitTip : "Drag to resize.",
33903     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33904     useSplitTips : false,
33905
33906     applyConfig : function(config){
33907         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33908         if(config.split){
33909             if(!this.split){
33910                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33911                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33912                 /** The SplitBar for this region 
33913                 * @type Roo.SplitBar */
33914                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33915                 this.split.on("moved", this.onSplitMove, this);
33916                 this.split.useShim = config.useShim === true;
33917                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33918                 if(this.useSplitTips){
33919                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33920                 }
33921                 if(config.collapsible){
33922                     this.split.el.on("dblclick", this.collapse,  this);
33923                 }
33924             }
33925             if(typeof config.minSize != "undefined"){
33926                 this.split.minSize = config.minSize;
33927             }
33928             if(typeof config.maxSize != "undefined"){
33929                 this.split.maxSize = config.maxSize;
33930             }
33931             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33932                 this.hideSplitter();
33933             }
33934         }
33935     },
33936
33937     getHMaxSize : function(){
33938          var cmax = this.config.maxSize || 10000;
33939          var center = this.mgr.getRegion("center");
33940          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33941     },
33942
33943     getVMaxSize : function(){
33944          var cmax = this.config.maxSize || 10000;
33945          var center = this.mgr.getRegion("center");
33946          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33947     },
33948
33949     onSplitMove : function(split, newSize){
33950         this.fireEvent("resized", this, newSize);
33951     },
33952     
33953     /** 
33954      * Returns the {@link Roo.SplitBar} for this region.
33955      * @return {Roo.SplitBar}
33956      */
33957     getSplitBar : function(){
33958         return this.split;
33959     },
33960     
33961     hide : function(){
33962         this.hideSplitter();
33963         Roo.SplitLayoutRegion.superclass.hide.call(this);
33964     },
33965
33966     hideSplitter : function(){
33967         if(this.split){
33968             this.split.el.setLocation(-2000,-2000);
33969             this.split.el.hide();
33970         }
33971     },
33972
33973     show : function(){
33974         if(this.split){
33975             this.split.el.show();
33976         }
33977         Roo.SplitLayoutRegion.superclass.show.call(this);
33978     },
33979     
33980     beforeSlide: function(){
33981         if(Roo.isGecko){// firefox overflow auto bug workaround
33982             this.bodyEl.clip();
33983             if(this.tabs) this.tabs.bodyEl.clip();
33984             if(this.activePanel){
33985                 this.activePanel.getEl().clip();
33986                 
33987                 if(this.activePanel.beforeSlide){
33988                     this.activePanel.beforeSlide();
33989                 }
33990             }
33991         }
33992     },
33993     
33994     afterSlide : function(){
33995         if(Roo.isGecko){// firefox overflow auto bug workaround
33996             this.bodyEl.unclip();
33997             if(this.tabs) this.tabs.bodyEl.unclip();
33998             if(this.activePanel){
33999                 this.activePanel.getEl().unclip();
34000                 if(this.activePanel.afterSlide){
34001                     this.activePanel.afterSlide();
34002                 }
34003             }
34004         }
34005     },
34006
34007     initAutoHide : function(){
34008         if(this.autoHide !== false){
34009             if(!this.autoHideHd){
34010                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34011                 this.autoHideHd = {
34012                     "mouseout": function(e){
34013                         if(!e.within(this.el, true)){
34014                             st.delay(500);
34015                         }
34016                     },
34017                     "mouseover" : function(e){
34018                         st.cancel();
34019                     },
34020                     scope : this
34021                 };
34022             }
34023             this.el.on(this.autoHideHd);
34024         }
34025     },
34026
34027     clearAutoHide : function(){
34028         if(this.autoHide !== false){
34029             this.el.un("mouseout", this.autoHideHd.mouseout);
34030             this.el.un("mouseover", this.autoHideHd.mouseover);
34031         }
34032     },
34033
34034     clearMonitor : function(){
34035         Roo.get(document).un("click", this.slideInIf, this);
34036     },
34037
34038     // these names are backwards but not changed for compat
34039     slideOut : function(){
34040         if(this.isSlid || this.el.hasActiveFx()){
34041             return;
34042         }
34043         this.isSlid = true;
34044         if(this.collapseBtn){
34045             this.collapseBtn.hide();
34046         }
34047         this.closeBtnState = this.closeBtn.getStyle('display');
34048         this.closeBtn.hide();
34049         if(this.stickBtn){
34050             this.stickBtn.show();
34051         }
34052         this.el.show();
34053         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34054         this.beforeSlide();
34055         this.el.setStyle("z-index", 10001);
34056         this.el.slideIn(this.getSlideAnchor(), {
34057             callback: function(){
34058                 this.afterSlide();
34059                 this.initAutoHide();
34060                 Roo.get(document).on("click", this.slideInIf, this);
34061                 this.fireEvent("slideshow", this);
34062             },
34063             scope: this,
34064             block: true
34065         });
34066     },
34067
34068     afterSlideIn : function(){
34069         this.clearAutoHide();
34070         this.isSlid = false;
34071         this.clearMonitor();
34072         this.el.setStyle("z-index", "");
34073         if(this.collapseBtn){
34074             this.collapseBtn.show();
34075         }
34076         this.closeBtn.setStyle('display', this.closeBtnState);
34077         if(this.stickBtn){
34078             this.stickBtn.hide();
34079         }
34080         this.fireEvent("slidehide", this);
34081     },
34082
34083     slideIn : function(cb){
34084         if(!this.isSlid || this.el.hasActiveFx()){
34085             Roo.callback(cb);
34086             return;
34087         }
34088         this.isSlid = false;
34089         this.beforeSlide();
34090         this.el.slideOut(this.getSlideAnchor(), {
34091             callback: function(){
34092                 this.el.setLeftTop(-10000, -10000);
34093                 this.afterSlide();
34094                 this.afterSlideIn();
34095                 Roo.callback(cb);
34096             },
34097             scope: this,
34098             block: true
34099         });
34100     },
34101     
34102     slideInIf : function(e){
34103         if(!e.within(this.el)){
34104             this.slideIn();
34105         }
34106     },
34107
34108     animateCollapse : function(){
34109         this.beforeSlide();
34110         this.el.setStyle("z-index", 20000);
34111         var anchor = this.getSlideAnchor();
34112         this.el.slideOut(anchor, {
34113             callback : function(){
34114                 this.el.setStyle("z-index", "");
34115                 this.collapsedEl.slideIn(anchor, {duration:.3});
34116                 this.afterSlide();
34117                 this.el.setLocation(-10000,-10000);
34118                 this.el.hide();
34119                 this.fireEvent("collapsed", this);
34120             },
34121             scope: this,
34122             block: true
34123         });
34124     },
34125
34126     animateExpand : function(){
34127         this.beforeSlide();
34128         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34129         this.el.setStyle("z-index", 20000);
34130         this.collapsedEl.hide({
34131             duration:.1
34132         });
34133         this.el.slideIn(this.getSlideAnchor(), {
34134             callback : function(){
34135                 this.el.setStyle("z-index", "");
34136                 this.afterSlide();
34137                 if(this.split){
34138                     this.split.el.show();
34139                 }
34140                 this.fireEvent("invalidated", this);
34141                 this.fireEvent("expanded", this);
34142             },
34143             scope: this,
34144             block: true
34145         });
34146     },
34147
34148     anchors : {
34149         "west" : "left",
34150         "east" : "right",
34151         "north" : "top",
34152         "south" : "bottom"
34153     },
34154
34155     sanchors : {
34156         "west" : "l",
34157         "east" : "r",
34158         "north" : "t",
34159         "south" : "b"
34160     },
34161
34162     canchors : {
34163         "west" : "tl-tr",
34164         "east" : "tr-tl",
34165         "north" : "tl-bl",
34166         "south" : "bl-tl"
34167     },
34168
34169     getAnchor : function(){
34170         return this.anchors[this.position];
34171     },
34172
34173     getCollapseAnchor : function(){
34174         return this.canchors[this.position];
34175     },
34176
34177     getSlideAnchor : function(){
34178         return this.sanchors[this.position];
34179     },
34180
34181     getAlignAdj : function(){
34182         var cm = this.cmargins;
34183         switch(this.position){
34184             case "west":
34185                 return [0, 0];
34186             break;
34187             case "east":
34188                 return [0, 0];
34189             break;
34190             case "north":
34191                 return [0, 0];
34192             break;
34193             case "south":
34194                 return [0, 0];
34195             break;
34196         }
34197     },
34198
34199     getExpandAdj : function(){
34200         var c = this.collapsedEl, cm = this.cmargins;
34201         switch(this.position){
34202             case "west":
34203                 return [-(cm.right+c.getWidth()+cm.left), 0];
34204             break;
34205             case "east":
34206                 return [cm.right+c.getWidth()+cm.left, 0];
34207             break;
34208             case "north":
34209                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34210             break;
34211             case "south":
34212                 return [0, cm.top+cm.bottom+c.getHeight()];
34213             break;
34214         }
34215     }
34216 });/*
34217  * Based on:
34218  * Ext JS Library 1.1.1
34219  * Copyright(c) 2006-2007, Ext JS, LLC.
34220  *
34221  * Originally Released Under LGPL - original licence link has changed is not relivant.
34222  *
34223  * Fork - LGPL
34224  * <script type="text/javascript">
34225  */
34226 /*
34227  * These classes are private internal classes
34228  */
34229 Roo.CenterLayoutRegion = function(mgr, config){
34230     Roo.LayoutRegion.call(this, mgr, config, "center");
34231     this.visible = true;
34232     this.minWidth = config.minWidth || 20;
34233     this.minHeight = config.minHeight || 20;
34234 };
34235
34236 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34237     hide : function(){
34238         // center panel can't be hidden
34239     },
34240     
34241     show : function(){
34242         // center panel can't be hidden
34243     },
34244     
34245     getMinWidth: function(){
34246         return this.minWidth;
34247     },
34248     
34249     getMinHeight: function(){
34250         return this.minHeight;
34251     }
34252 });
34253
34254
34255 Roo.NorthLayoutRegion = function(mgr, config){
34256     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34257     if(this.split){
34258         this.split.placement = Roo.SplitBar.TOP;
34259         this.split.orientation = Roo.SplitBar.VERTICAL;
34260         this.split.el.addClass("x-layout-split-v");
34261     }
34262     var size = config.initialSize || config.height;
34263     if(typeof size != "undefined"){
34264         this.el.setHeight(size);
34265     }
34266 };
34267 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34268     orientation: Roo.SplitBar.VERTICAL,
34269     getBox : function(){
34270         if(this.collapsed){
34271             return this.collapsedEl.getBox();
34272         }
34273         var box = this.el.getBox();
34274         if(this.split){
34275             box.height += this.split.el.getHeight();
34276         }
34277         return box;
34278     },
34279     
34280     updateBox : function(box){
34281         if(this.split && !this.collapsed){
34282             box.height -= this.split.el.getHeight();
34283             this.split.el.setLeft(box.x);
34284             this.split.el.setTop(box.y+box.height);
34285             this.split.el.setWidth(box.width);
34286         }
34287         if(this.collapsed){
34288             this.updateBody(box.width, null);
34289         }
34290         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34291     }
34292 });
34293
34294 Roo.SouthLayoutRegion = function(mgr, config){
34295     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34296     if(this.split){
34297         this.split.placement = Roo.SplitBar.BOTTOM;
34298         this.split.orientation = Roo.SplitBar.VERTICAL;
34299         this.split.el.addClass("x-layout-split-v");
34300     }
34301     var size = config.initialSize || config.height;
34302     if(typeof size != "undefined"){
34303         this.el.setHeight(size);
34304     }
34305 };
34306 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34307     orientation: Roo.SplitBar.VERTICAL,
34308     getBox : function(){
34309         if(this.collapsed){
34310             return this.collapsedEl.getBox();
34311         }
34312         var box = this.el.getBox();
34313         if(this.split){
34314             var sh = this.split.el.getHeight();
34315             box.height += sh;
34316             box.y -= sh;
34317         }
34318         return box;
34319     },
34320     
34321     updateBox : function(box){
34322         if(this.split && !this.collapsed){
34323             var sh = this.split.el.getHeight();
34324             box.height -= sh;
34325             box.y += sh;
34326             this.split.el.setLeft(box.x);
34327             this.split.el.setTop(box.y-sh);
34328             this.split.el.setWidth(box.width);
34329         }
34330         if(this.collapsed){
34331             this.updateBody(box.width, null);
34332         }
34333         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34334     }
34335 });
34336
34337 Roo.EastLayoutRegion = function(mgr, config){
34338     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34339     if(this.split){
34340         this.split.placement = Roo.SplitBar.RIGHT;
34341         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34342         this.split.el.addClass("x-layout-split-h");
34343     }
34344     var size = config.initialSize || config.width;
34345     if(typeof size != "undefined"){
34346         this.el.setWidth(size);
34347     }
34348 };
34349 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34350     orientation: Roo.SplitBar.HORIZONTAL,
34351     getBox : function(){
34352         if(this.collapsed){
34353             return this.collapsedEl.getBox();
34354         }
34355         var box = this.el.getBox();
34356         if(this.split){
34357             var sw = this.split.el.getWidth();
34358             box.width += sw;
34359             box.x -= sw;
34360         }
34361         return box;
34362     },
34363
34364     updateBox : function(box){
34365         if(this.split && !this.collapsed){
34366             var sw = this.split.el.getWidth();
34367             box.width -= sw;
34368             this.split.el.setLeft(box.x);
34369             this.split.el.setTop(box.y);
34370             this.split.el.setHeight(box.height);
34371             box.x += sw;
34372         }
34373         if(this.collapsed){
34374             this.updateBody(null, box.height);
34375         }
34376         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34377     }
34378 });
34379
34380 Roo.WestLayoutRegion = function(mgr, config){
34381     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34382     if(this.split){
34383         this.split.placement = Roo.SplitBar.LEFT;
34384         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34385         this.split.el.addClass("x-layout-split-h");
34386     }
34387     var size = config.initialSize || config.width;
34388     if(typeof size != "undefined"){
34389         this.el.setWidth(size);
34390     }
34391 };
34392 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34393     orientation: Roo.SplitBar.HORIZONTAL,
34394     getBox : function(){
34395         if(this.collapsed){
34396             return this.collapsedEl.getBox();
34397         }
34398         var box = this.el.getBox();
34399         if(this.split){
34400             box.width += this.split.el.getWidth();
34401         }
34402         return box;
34403     },
34404     
34405     updateBox : function(box){
34406         if(this.split && !this.collapsed){
34407             var sw = this.split.el.getWidth();
34408             box.width -= sw;
34409             this.split.el.setLeft(box.x+box.width);
34410             this.split.el.setTop(box.y);
34411             this.split.el.setHeight(box.height);
34412         }
34413         if(this.collapsed){
34414             this.updateBody(null, box.height);
34415         }
34416         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34417     }
34418 });
34419 /*
34420  * Based on:
34421  * Ext JS Library 1.1.1
34422  * Copyright(c) 2006-2007, Ext JS, LLC.
34423  *
34424  * Originally Released Under LGPL - original licence link has changed is not relivant.
34425  *
34426  * Fork - LGPL
34427  * <script type="text/javascript">
34428  */
34429  
34430  
34431 /*
34432  * Private internal class for reading and applying state
34433  */
34434 Roo.LayoutStateManager = function(layout){
34435      // default empty state
34436      this.state = {
34437         north: {},
34438         south: {},
34439         east: {},
34440         west: {}       
34441     };
34442 };
34443
34444 Roo.LayoutStateManager.prototype = {
34445     init : function(layout, provider){
34446         this.provider = provider;
34447         var state = provider.get(layout.id+"-layout-state");
34448         if(state){
34449             var wasUpdating = layout.isUpdating();
34450             if(!wasUpdating){
34451                 layout.beginUpdate();
34452             }
34453             for(var key in state){
34454                 if(typeof state[key] != "function"){
34455                     var rstate = state[key];
34456                     var r = layout.getRegion(key);
34457                     if(r && rstate){
34458                         if(rstate.size){
34459                             r.resizeTo(rstate.size);
34460                         }
34461                         if(rstate.collapsed == true){
34462                             r.collapse(true);
34463                         }else{
34464                             r.expand(null, true);
34465                         }
34466                     }
34467                 }
34468             }
34469             if(!wasUpdating){
34470                 layout.endUpdate();
34471             }
34472             this.state = state; 
34473         }
34474         this.layout = layout;
34475         layout.on("regionresized", this.onRegionResized, this);
34476         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34477         layout.on("regionexpanded", this.onRegionExpanded, this);
34478     },
34479     
34480     storeState : function(){
34481         this.provider.set(this.layout.id+"-layout-state", this.state);
34482     },
34483     
34484     onRegionResized : function(region, newSize){
34485         this.state[region.getPosition()].size = newSize;
34486         this.storeState();
34487     },
34488     
34489     onRegionCollapsed : function(region){
34490         this.state[region.getPosition()].collapsed = true;
34491         this.storeState();
34492     },
34493     
34494     onRegionExpanded : function(region){
34495         this.state[region.getPosition()].collapsed = false;
34496         this.storeState();
34497     }
34498 };/*
34499  * Based on:
34500  * Ext JS Library 1.1.1
34501  * Copyright(c) 2006-2007, Ext JS, LLC.
34502  *
34503  * Originally Released Under LGPL - original licence link has changed is not relivant.
34504  *
34505  * Fork - LGPL
34506  * <script type="text/javascript">
34507  */
34508 /**
34509  * @class Roo.ContentPanel
34510  * @extends Roo.util.Observable
34511  * A basic ContentPanel element.
34512  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34513  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34514  * @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
34515  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34516  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34517  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34518  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34519  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34520  * @cfg {String} title          The title for this panel
34521  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34522  * @cfg {String} url            Calls {@link #setUrl} with this value
34523  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34524  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34525  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34526  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34527
34528  * @constructor
34529  * Create a new ContentPanel.
34530  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34531  * @param {String/Object} config A string to set only the title or a config object
34532  * @param {String} content (optional) Set the HTML content for this panel
34533  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34534  */
34535 Roo.ContentPanel = function(el, config, content){
34536     
34537      
34538     /*
34539     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34540         config = el;
34541         el = Roo.id();
34542     }
34543     if (config && config.parentLayout) { 
34544         el = config.parentLayout.el.createChild(); 
34545     }
34546     */
34547     if(el.autoCreate){ // xtype is available if this is called from factory
34548         config = el;
34549         el = Roo.id();
34550     }
34551     this.el = Roo.get(el);
34552     if(!this.el && config && config.autoCreate){
34553         if(typeof config.autoCreate == "object"){
34554             if(!config.autoCreate.id){
34555                 config.autoCreate.id = config.id||el;
34556             }
34557             this.el = Roo.DomHelper.append(document.body,
34558                         config.autoCreate, true);
34559         }else{
34560             this.el = Roo.DomHelper.append(document.body,
34561                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34562         }
34563     }
34564     this.closable = false;
34565     this.loaded = false;
34566     this.active = false;
34567     if(typeof config == "string"){
34568         this.title = config;
34569     }else{
34570         Roo.apply(this, config);
34571     }
34572     
34573     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34574         this.wrapEl = this.el.wrap();
34575         this.toolbar.container = this.el.insertSibling(false, 'before');
34576         this.toolbar = new Roo.Toolbar(this.toolbar);
34577     }
34578     
34579     // xtype created footer. - not sure if will work as we normally have to render first..
34580     if (this.footer && !this.footer.el && this.footer.xtype) {
34581         if (!this.wrapEl) {
34582             this.wrapEl = this.el.wrap();
34583         }
34584     
34585         this.footer.container = this.wrapEl.createChild();
34586          
34587         this.footer = Roo.factory(this.footer, Roo);
34588         
34589     }
34590     
34591     if(this.resizeEl){
34592         this.resizeEl = Roo.get(this.resizeEl, true);
34593     }else{
34594         this.resizeEl = this.el;
34595     }
34596     // handle view.xtype
34597     
34598  
34599     
34600     
34601     this.addEvents({
34602         /**
34603          * @event activate
34604          * Fires when this panel is activated. 
34605          * @param {Roo.ContentPanel} this
34606          */
34607         "activate" : true,
34608         /**
34609          * @event deactivate
34610          * Fires when this panel is activated. 
34611          * @param {Roo.ContentPanel} this
34612          */
34613         "deactivate" : true,
34614
34615         /**
34616          * @event resize
34617          * Fires when this panel is resized if fitToFrame is true.
34618          * @param {Roo.ContentPanel} this
34619          * @param {Number} width The width after any component adjustments
34620          * @param {Number} height The height after any component adjustments
34621          */
34622         "resize" : true,
34623         
34624          /**
34625          * @event render
34626          * Fires when this tab is created
34627          * @param {Roo.ContentPanel} this
34628          */
34629         "render" : true
34630         
34631         
34632         
34633     });
34634     
34635
34636     
34637     
34638     if(this.autoScroll){
34639         this.resizeEl.setStyle("overflow", "auto");
34640     } else {
34641         // fix randome scrolling
34642         this.el.on('scroll', function() {
34643             Roo.log('fix random scolling');
34644             this.scrollTo('top',0); 
34645         });
34646     }
34647     content = content || this.content;
34648     if(content){
34649         this.setContent(content);
34650     }
34651     if(config && config.url){
34652         this.setUrl(this.url, this.params, this.loadOnce);
34653     }
34654     
34655     
34656     
34657     Roo.ContentPanel.superclass.constructor.call(this);
34658     
34659     if (this.view && typeof(this.view.xtype) != 'undefined') {
34660         this.view.el = this.el.appendChild(document.createElement("div"));
34661         this.view = Roo.factory(this.view); 
34662         this.view.render  &&  this.view.render(false, '');  
34663     }
34664     
34665     
34666     this.fireEvent('render', this);
34667 };
34668
34669 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34670     tabTip:'',
34671     setRegion : function(region){
34672         this.region = region;
34673         if(region){
34674            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34675         }else{
34676            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34677         } 
34678     },
34679     
34680     /**
34681      * Returns the toolbar for this Panel if one was configured. 
34682      * @return {Roo.Toolbar} 
34683      */
34684     getToolbar : function(){
34685         return this.toolbar;
34686     },
34687     
34688     setActiveState : function(active){
34689         this.active = active;
34690         if(!active){
34691             this.fireEvent("deactivate", this);
34692         }else{
34693             this.fireEvent("activate", this);
34694         }
34695     },
34696     /**
34697      * Updates this panel's element
34698      * @param {String} content The new content
34699      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34700     */
34701     setContent : function(content, loadScripts){
34702         this.el.update(content, loadScripts);
34703     },
34704
34705     ignoreResize : function(w, h){
34706         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34707             return true;
34708         }else{
34709             this.lastSize = {width: w, height: h};
34710             return false;
34711         }
34712     },
34713     /**
34714      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34715      * @return {Roo.UpdateManager} The UpdateManager
34716      */
34717     getUpdateManager : function(){
34718         return this.el.getUpdateManager();
34719     },
34720      /**
34721      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34722      * @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:
34723 <pre><code>
34724 panel.load({
34725     url: "your-url.php",
34726     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34727     callback: yourFunction,
34728     scope: yourObject, //(optional scope)
34729     discardUrl: false,
34730     nocache: false,
34731     text: "Loading...",
34732     timeout: 30,
34733     scripts: false
34734 });
34735 </code></pre>
34736      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34737      * 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.
34738      * @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}
34739      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34740      * @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.
34741      * @return {Roo.ContentPanel} this
34742      */
34743     load : function(){
34744         var um = this.el.getUpdateManager();
34745         um.update.apply(um, arguments);
34746         return this;
34747     },
34748
34749
34750     /**
34751      * 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.
34752      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34753      * @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)
34754      * @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)
34755      * @return {Roo.UpdateManager} The UpdateManager
34756      */
34757     setUrl : function(url, params, loadOnce){
34758         if(this.refreshDelegate){
34759             this.removeListener("activate", this.refreshDelegate);
34760         }
34761         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34762         this.on("activate", this.refreshDelegate);
34763         return this.el.getUpdateManager();
34764     },
34765     
34766     _handleRefresh : function(url, params, loadOnce){
34767         if(!loadOnce || !this.loaded){
34768             var updater = this.el.getUpdateManager();
34769             updater.update(url, params, this._setLoaded.createDelegate(this));
34770         }
34771     },
34772     
34773     _setLoaded : function(){
34774         this.loaded = true;
34775     }, 
34776     
34777     /**
34778      * Returns this panel's id
34779      * @return {String} 
34780      */
34781     getId : function(){
34782         return this.el.id;
34783     },
34784     
34785     /** 
34786      * Returns this panel's element - used by regiosn to add.
34787      * @return {Roo.Element} 
34788      */
34789     getEl : function(){
34790         return this.wrapEl || this.el;
34791     },
34792     
34793     adjustForComponents : function(width, height)
34794     {
34795         //Roo.log('adjustForComponents ');
34796         if(this.resizeEl != this.el){
34797             width -= this.el.getFrameWidth('lr');
34798             height -= this.el.getFrameWidth('tb');
34799         }
34800         if(this.toolbar){
34801             var te = this.toolbar.getEl();
34802             height -= te.getHeight();
34803             te.setWidth(width);
34804         }
34805         if(this.footer){
34806             var te = this.footer.getEl();
34807             Roo.log("footer:" + te.getHeight());
34808             
34809             height -= te.getHeight();
34810             te.setWidth(width);
34811         }
34812         
34813         
34814         if(this.adjustments){
34815             width += this.adjustments[0];
34816             height += this.adjustments[1];
34817         }
34818         return {"width": width, "height": height};
34819     },
34820     
34821     setSize : function(width, height){
34822         if(this.fitToFrame && !this.ignoreResize(width, height)){
34823             if(this.fitContainer && this.resizeEl != this.el){
34824                 this.el.setSize(width, height);
34825             }
34826             var size = this.adjustForComponents(width, height);
34827             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34828             this.fireEvent('resize', this, size.width, size.height);
34829         }
34830     },
34831     
34832     /**
34833      * Returns this panel's title
34834      * @return {String} 
34835      */
34836     getTitle : function(){
34837         return this.title;
34838     },
34839     
34840     /**
34841      * Set this panel's title
34842      * @param {String} title
34843      */
34844     setTitle : function(title){
34845         this.title = title;
34846         if(this.region){
34847             this.region.updatePanelTitle(this, title);
34848         }
34849     },
34850     
34851     /**
34852      * Returns true is this panel was configured to be closable
34853      * @return {Boolean} 
34854      */
34855     isClosable : function(){
34856         return this.closable;
34857     },
34858     
34859     beforeSlide : function(){
34860         this.el.clip();
34861         this.resizeEl.clip();
34862     },
34863     
34864     afterSlide : function(){
34865         this.el.unclip();
34866         this.resizeEl.unclip();
34867     },
34868     
34869     /**
34870      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34871      *   Will fail silently if the {@link #setUrl} method has not been called.
34872      *   This does not activate the panel, just updates its content.
34873      */
34874     refresh : function(){
34875         if(this.refreshDelegate){
34876            this.loaded = false;
34877            this.refreshDelegate();
34878         }
34879     },
34880     
34881     /**
34882      * Destroys this panel
34883      */
34884     destroy : function(){
34885         this.el.removeAllListeners();
34886         var tempEl = document.createElement("span");
34887         tempEl.appendChild(this.el.dom);
34888         tempEl.innerHTML = "";
34889         this.el.remove();
34890         this.el = null;
34891     },
34892     
34893     /**
34894      * form - if the content panel contains a form - this is a reference to it.
34895      * @type {Roo.form.Form}
34896      */
34897     form : false,
34898     /**
34899      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34900      *    This contains a reference to it.
34901      * @type {Roo.View}
34902      */
34903     view : false,
34904     
34905       /**
34906      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34907      * <pre><code>
34908
34909 layout.addxtype({
34910        xtype : 'Form',
34911        items: [ .... ]
34912    }
34913 );
34914
34915 </code></pre>
34916      * @param {Object} cfg Xtype definition of item to add.
34917      */
34918     
34919     addxtype : function(cfg) {
34920         // add form..
34921         if (cfg.xtype.match(/^Form$/)) {
34922             
34923             var el;
34924             //if (this.footer) {
34925             //    el = this.footer.container.insertSibling(false, 'before');
34926             //} else {
34927                 el = this.el.createChild();
34928             //}
34929
34930             this.form = new  Roo.form.Form(cfg);
34931             
34932             
34933             if ( this.form.allItems.length) this.form.render(el.dom);
34934             return this.form;
34935         }
34936         // should only have one of theses..
34937         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34938             // views.. should not be just added - used named prop 'view''
34939             
34940             cfg.el = this.el.appendChild(document.createElement("div"));
34941             // factory?
34942             
34943             var ret = new Roo.factory(cfg);
34944              
34945              ret.render && ret.render(false, ''); // render blank..
34946             this.view = ret;
34947             return ret;
34948         }
34949         return false;
34950     }
34951 });
34952
34953 /**
34954  * @class Roo.GridPanel
34955  * @extends Roo.ContentPanel
34956  * @constructor
34957  * Create a new GridPanel.
34958  * @param {Roo.grid.Grid} grid The grid for this panel
34959  * @param {String/Object} config A string to set only the panel's title, or a config object
34960  */
34961 Roo.GridPanel = function(grid, config){
34962     
34963   
34964     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34965         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34966         
34967     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34968     
34969     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34970     
34971     if(this.toolbar){
34972         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34973     }
34974     // xtype created footer. - not sure if will work as we normally have to render first..
34975     if (this.footer && !this.footer.el && this.footer.xtype) {
34976         
34977         this.footer.container = this.grid.getView().getFooterPanel(true);
34978         this.footer.dataSource = this.grid.dataSource;
34979         this.footer = Roo.factory(this.footer, Roo);
34980         
34981     }
34982     
34983     grid.monitorWindowResize = false; // turn off autosizing
34984     grid.autoHeight = false;
34985     grid.autoWidth = false;
34986     this.grid = grid;
34987     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34988 };
34989
34990 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34991     getId : function(){
34992         return this.grid.id;
34993     },
34994     
34995     /**
34996      * Returns the grid for this panel
34997      * @return {Roo.grid.Grid} 
34998      */
34999     getGrid : function(){
35000         return this.grid;    
35001     },
35002     
35003     setSize : function(width, height){
35004         if(!this.ignoreResize(width, height)){
35005             var grid = this.grid;
35006             var size = this.adjustForComponents(width, height);
35007             grid.getGridEl().setSize(size.width, size.height);
35008             grid.autoSize();
35009         }
35010     },
35011     
35012     beforeSlide : function(){
35013         this.grid.getView().scroller.clip();
35014     },
35015     
35016     afterSlide : function(){
35017         this.grid.getView().scroller.unclip();
35018     },
35019     
35020     destroy : function(){
35021         this.grid.destroy();
35022         delete this.grid;
35023         Roo.GridPanel.superclass.destroy.call(this); 
35024     }
35025 });
35026
35027
35028 /**
35029  * @class Roo.NestedLayoutPanel
35030  * @extends Roo.ContentPanel
35031  * @constructor
35032  * Create a new NestedLayoutPanel.
35033  * 
35034  * 
35035  * @param {Roo.BorderLayout} layout The layout for this panel
35036  * @param {String/Object} config A string to set only the title or a config object
35037  */
35038 Roo.NestedLayoutPanel = function(layout, config)
35039 {
35040     // construct with only one argument..
35041     /* FIXME - implement nicer consturctors
35042     if (layout.layout) {
35043         config = layout;
35044         layout = config.layout;
35045         delete config.layout;
35046     }
35047     if (layout.xtype && !layout.getEl) {
35048         // then layout needs constructing..
35049         layout = Roo.factory(layout, Roo);
35050     }
35051     */
35052     
35053     
35054     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35055     
35056     layout.monitorWindowResize = false; // turn off autosizing
35057     this.layout = layout;
35058     this.layout.getEl().addClass("x-layout-nested-layout");
35059     
35060     
35061     
35062     
35063 };
35064
35065 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35066
35067     setSize : function(width, height){
35068         if(!this.ignoreResize(width, height)){
35069             var size = this.adjustForComponents(width, height);
35070             var el = this.layout.getEl();
35071             el.setSize(size.width, size.height);
35072             var touch = el.dom.offsetWidth;
35073             this.layout.layout();
35074             // ie requires a double layout on the first pass
35075             if(Roo.isIE && !this.initialized){
35076                 this.initialized = true;
35077                 this.layout.layout();
35078             }
35079         }
35080     },
35081     
35082     // activate all subpanels if not currently active..
35083     
35084     setActiveState : function(active){
35085         this.active = active;
35086         if(!active){
35087             this.fireEvent("deactivate", this);
35088             return;
35089         }
35090         
35091         this.fireEvent("activate", this);
35092         // not sure if this should happen before or after..
35093         if (!this.layout) {
35094             return; // should not happen..
35095         }
35096         var reg = false;
35097         for (var r in this.layout.regions) {
35098             reg = this.layout.getRegion(r);
35099             if (reg.getActivePanel()) {
35100                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35101                 reg.setActivePanel(reg.getActivePanel());
35102                 continue;
35103             }
35104             if (!reg.panels.length) {
35105                 continue;
35106             }
35107             reg.showPanel(reg.getPanel(0));
35108         }
35109         
35110         
35111         
35112         
35113     },
35114     
35115     /**
35116      * Returns the nested BorderLayout for this panel
35117      * @return {Roo.BorderLayout} 
35118      */
35119     getLayout : function(){
35120         return this.layout;
35121     },
35122     
35123      /**
35124      * Adds a xtype elements to the layout of the nested panel
35125      * <pre><code>
35126
35127 panel.addxtype({
35128        xtype : 'ContentPanel',
35129        region: 'west',
35130        items: [ .... ]
35131    }
35132 );
35133
35134 panel.addxtype({
35135         xtype : 'NestedLayoutPanel',
35136         region: 'west',
35137         layout: {
35138            center: { },
35139            west: { }   
35140         },
35141         items : [ ... list of content panels or nested layout panels.. ]
35142    }
35143 );
35144 </code></pre>
35145      * @param {Object} cfg Xtype definition of item to add.
35146      */
35147     addxtype : function(cfg) {
35148         return this.layout.addxtype(cfg);
35149     
35150     }
35151 });
35152
35153 Roo.ScrollPanel = function(el, config, content){
35154     config = config || {};
35155     config.fitToFrame = true;
35156     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35157     
35158     this.el.dom.style.overflow = "hidden";
35159     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35160     this.el.removeClass("x-layout-inactive-content");
35161     this.el.on("mousewheel", this.onWheel, this);
35162
35163     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35164     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35165     up.unselectable(); down.unselectable();
35166     up.on("click", this.scrollUp, this);
35167     down.on("click", this.scrollDown, this);
35168     up.addClassOnOver("x-scroller-btn-over");
35169     down.addClassOnOver("x-scroller-btn-over");
35170     up.addClassOnClick("x-scroller-btn-click");
35171     down.addClassOnClick("x-scroller-btn-click");
35172     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35173
35174     this.resizeEl = this.el;
35175     this.el = wrap; this.up = up; this.down = down;
35176 };
35177
35178 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35179     increment : 100,
35180     wheelIncrement : 5,
35181     scrollUp : function(){
35182         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35183     },
35184
35185     scrollDown : function(){
35186         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35187     },
35188
35189     afterScroll : function(){
35190         var el = this.resizeEl;
35191         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35192         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35193         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35194     },
35195
35196     setSize : function(){
35197         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35198         this.afterScroll();
35199     },
35200
35201     onWheel : function(e){
35202         var d = e.getWheelDelta();
35203         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35204         this.afterScroll();
35205         e.stopEvent();
35206     },
35207
35208     setContent : function(content, loadScripts){
35209         this.resizeEl.update(content, loadScripts);
35210     }
35211
35212 });
35213
35214
35215
35216
35217
35218
35219
35220
35221
35222 /**
35223  * @class Roo.TreePanel
35224  * @extends Roo.ContentPanel
35225  * @constructor
35226  * Create a new TreePanel. - defaults to fit/scoll contents.
35227  * @param {String/Object} config A string to set only the panel's title, or a config object
35228  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35229  */
35230 Roo.TreePanel = function(config){
35231     var el = config.el;
35232     var tree = config.tree;
35233     delete config.tree; 
35234     delete config.el; // hopefull!
35235     
35236     // wrapper for IE7 strict & safari scroll issue
35237     
35238     var treeEl = el.createChild();
35239     config.resizeEl = treeEl;
35240     
35241     
35242     
35243     Roo.TreePanel.superclass.constructor.call(this, el, config);
35244  
35245  
35246     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35247     //console.log(tree);
35248     this.on('activate', function()
35249     {
35250         if (this.tree.rendered) {
35251             return;
35252         }
35253         //console.log('render tree');
35254         this.tree.render();
35255     });
35256     // this should not be needed.. - it's actually the 'el' that resizes?
35257     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35258     
35259     //this.on('resize',  function (cp, w, h) {
35260     //        this.tree.innerCt.setWidth(w);
35261     //        this.tree.innerCt.setHeight(h);
35262     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35263     //});
35264
35265         
35266     
35267 };
35268
35269 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35270     fitToFrame : true,
35271     autoScroll : true
35272 });
35273
35274
35275
35276
35277
35278
35279
35280
35281
35282
35283
35284 /*
35285  * Based on:
35286  * Ext JS Library 1.1.1
35287  * Copyright(c) 2006-2007, Ext JS, LLC.
35288  *
35289  * Originally Released Under LGPL - original licence link has changed is not relivant.
35290  *
35291  * Fork - LGPL
35292  * <script type="text/javascript">
35293  */
35294  
35295
35296 /**
35297  * @class Roo.ReaderLayout
35298  * @extends Roo.BorderLayout
35299  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35300  * center region containing two nested regions (a top one for a list view and one for item preview below),
35301  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35302  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35303  * expedites the setup of the overall layout and regions for this common application style.
35304  * Example:
35305  <pre><code>
35306 var reader = new Roo.ReaderLayout();
35307 var CP = Roo.ContentPanel;  // shortcut for adding
35308
35309 reader.beginUpdate();
35310 reader.add("north", new CP("north", "North"));
35311 reader.add("west", new CP("west", {title: "West"}));
35312 reader.add("east", new CP("east", {title: "East"}));
35313
35314 reader.regions.listView.add(new CP("listView", "List"));
35315 reader.regions.preview.add(new CP("preview", "Preview"));
35316 reader.endUpdate();
35317 </code></pre>
35318 * @constructor
35319 * Create a new ReaderLayout
35320 * @param {Object} config Configuration options
35321 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35322 * document.body if omitted)
35323 */
35324 Roo.ReaderLayout = function(config, renderTo){
35325     var c = config || {size:{}};
35326     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35327         north: c.north !== false ? Roo.apply({
35328             split:false,
35329             initialSize: 32,
35330             titlebar: false
35331         }, c.north) : false,
35332         west: c.west !== false ? Roo.apply({
35333             split:true,
35334             initialSize: 200,
35335             minSize: 175,
35336             maxSize: 400,
35337             titlebar: true,
35338             collapsible: true,
35339             animate: true,
35340             margins:{left:5,right:0,bottom:5,top:5},
35341             cmargins:{left:5,right:5,bottom:5,top:5}
35342         }, c.west) : false,
35343         east: c.east !== false ? Roo.apply({
35344             split:true,
35345             initialSize: 200,
35346             minSize: 175,
35347             maxSize: 400,
35348             titlebar: true,
35349             collapsible: true,
35350             animate: true,
35351             margins:{left:0,right:5,bottom:5,top:5},
35352             cmargins:{left:5,right:5,bottom:5,top:5}
35353         }, c.east) : false,
35354         center: Roo.apply({
35355             tabPosition: 'top',
35356             autoScroll:false,
35357             closeOnTab: true,
35358             titlebar:false,
35359             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35360         }, c.center)
35361     });
35362
35363     this.el.addClass('x-reader');
35364
35365     this.beginUpdate();
35366
35367     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35368         south: c.preview !== false ? Roo.apply({
35369             split:true,
35370             initialSize: 200,
35371             minSize: 100,
35372             autoScroll:true,
35373             collapsible:true,
35374             titlebar: true,
35375             cmargins:{top:5,left:0, right:0, bottom:0}
35376         }, c.preview) : false,
35377         center: Roo.apply({
35378             autoScroll:false,
35379             titlebar:false,
35380             minHeight:200
35381         }, c.listView)
35382     });
35383     this.add('center', new Roo.NestedLayoutPanel(inner,
35384             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35385
35386     this.endUpdate();
35387
35388     this.regions.preview = inner.getRegion('south');
35389     this.regions.listView = inner.getRegion('center');
35390 };
35391
35392 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35393  * Based on:
35394  * Ext JS Library 1.1.1
35395  * Copyright(c) 2006-2007, Ext JS, LLC.
35396  *
35397  * Originally Released Under LGPL - original licence link has changed is not relivant.
35398  *
35399  * Fork - LGPL
35400  * <script type="text/javascript">
35401  */
35402  
35403 /**
35404  * @class Roo.grid.Grid
35405  * @extends Roo.util.Observable
35406  * This class represents the primary interface of a component based grid control.
35407  * <br><br>Usage:<pre><code>
35408  var grid = new Roo.grid.Grid("my-container-id", {
35409      ds: myDataStore,
35410      cm: myColModel,
35411      selModel: mySelectionModel,
35412      autoSizeColumns: true,
35413      monitorWindowResize: false,
35414      trackMouseOver: true
35415  });
35416  // set any options
35417  grid.render();
35418  * </code></pre>
35419  * <b>Common Problems:</b><br/>
35420  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35421  * element will correct this<br/>
35422  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35423  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35424  * are unpredictable.<br/>
35425  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35426  * grid to calculate dimensions/offsets.<br/>
35427   * @constructor
35428  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35429  * The container MUST have some type of size defined for the grid to fill. The container will be
35430  * automatically set to position relative if it isn't already.
35431  * @param {Object} config A config object that sets properties on this grid.
35432  */
35433 Roo.grid.Grid = function(container, config){
35434         // initialize the container
35435         this.container = Roo.get(container);
35436         this.container.update("");
35437         this.container.setStyle("overflow", "hidden");
35438     this.container.addClass('x-grid-container');
35439
35440     this.id = this.container.id;
35441
35442     Roo.apply(this, config);
35443     // check and correct shorthanded configs
35444     if(this.ds){
35445         this.dataSource = this.ds;
35446         delete this.ds;
35447     }
35448     if(this.cm){
35449         this.colModel = this.cm;
35450         delete this.cm;
35451     }
35452     if(this.sm){
35453         this.selModel = this.sm;
35454         delete this.sm;
35455     }
35456
35457     if (this.selModel) {
35458         this.selModel = Roo.factory(this.selModel, Roo.grid);
35459         this.sm = this.selModel;
35460         this.sm.xmodule = this.xmodule || false;
35461     }
35462     if (typeof(this.colModel.config) == 'undefined') {
35463         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35464         this.cm = this.colModel;
35465         this.cm.xmodule = this.xmodule || false;
35466     }
35467     if (this.dataSource) {
35468         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35469         this.ds = this.dataSource;
35470         this.ds.xmodule = this.xmodule || false;
35471          
35472     }
35473     
35474     
35475     
35476     if(this.width){
35477         this.container.setWidth(this.width);
35478     }
35479
35480     if(this.height){
35481         this.container.setHeight(this.height);
35482     }
35483     /** @private */
35484         this.addEvents({
35485         // raw events
35486         /**
35487          * @event click
35488          * The raw click event for the entire grid.
35489          * @param {Roo.EventObject} e
35490          */
35491         "click" : true,
35492         /**
35493          * @event dblclick
35494          * The raw dblclick event for the entire grid.
35495          * @param {Roo.EventObject} e
35496          */
35497         "dblclick" : true,
35498         /**
35499          * @event contextmenu
35500          * The raw contextmenu event for the entire grid.
35501          * @param {Roo.EventObject} e
35502          */
35503         "contextmenu" : true,
35504         /**
35505          * @event mousedown
35506          * The raw mousedown event for the entire grid.
35507          * @param {Roo.EventObject} e
35508          */
35509         "mousedown" : true,
35510         /**
35511          * @event mouseup
35512          * The raw mouseup event for the entire grid.
35513          * @param {Roo.EventObject} e
35514          */
35515         "mouseup" : true,
35516         /**
35517          * @event mouseover
35518          * The raw mouseover event for the entire grid.
35519          * @param {Roo.EventObject} e
35520          */
35521         "mouseover" : true,
35522         /**
35523          * @event mouseout
35524          * The raw mouseout event for the entire grid.
35525          * @param {Roo.EventObject} e
35526          */
35527         "mouseout" : true,
35528         /**
35529          * @event keypress
35530          * The raw keypress event for the entire grid.
35531          * @param {Roo.EventObject} e
35532          */
35533         "keypress" : true,
35534         /**
35535          * @event keydown
35536          * The raw keydown event for the entire grid.
35537          * @param {Roo.EventObject} e
35538          */
35539         "keydown" : true,
35540
35541         // custom events
35542
35543         /**
35544          * @event cellclick
35545          * Fires when a cell is clicked
35546          * @param {Grid} this
35547          * @param {Number} rowIndex
35548          * @param {Number} columnIndex
35549          * @param {Roo.EventObject} e
35550          */
35551         "cellclick" : true,
35552         /**
35553          * @event celldblclick
35554          * Fires when a cell is double clicked
35555          * @param {Grid} this
35556          * @param {Number} rowIndex
35557          * @param {Number} columnIndex
35558          * @param {Roo.EventObject} e
35559          */
35560         "celldblclick" : true,
35561         /**
35562          * @event rowclick
35563          * Fires when a row is clicked
35564          * @param {Grid} this
35565          * @param {Number} rowIndex
35566          * @param {Roo.EventObject} e
35567          */
35568         "rowclick" : true,
35569         /**
35570          * @event rowdblclick
35571          * Fires when a row is double clicked
35572          * @param {Grid} this
35573          * @param {Number} rowIndex
35574          * @param {Roo.EventObject} e
35575          */
35576         "rowdblclick" : true,
35577         /**
35578          * @event headerclick
35579          * Fires when a header is clicked
35580          * @param {Grid} this
35581          * @param {Number} columnIndex
35582          * @param {Roo.EventObject} e
35583          */
35584         "headerclick" : true,
35585         /**
35586          * @event headerdblclick
35587          * Fires when a header cell is double clicked
35588          * @param {Grid} this
35589          * @param {Number} columnIndex
35590          * @param {Roo.EventObject} e
35591          */
35592         "headerdblclick" : true,
35593         /**
35594          * @event rowcontextmenu
35595          * Fires when a row is right clicked
35596          * @param {Grid} this
35597          * @param {Number} rowIndex
35598          * @param {Roo.EventObject} e
35599          */
35600         "rowcontextmenu" : true,
35601         /**
35602          * @event cellcontextmenu
35603          * Fires when a cell is right clicked
35604          * @param {Grid} this
35605          * @param {Number} rowIndex
35606          * @param {Number} cellIndex
35607          * @param {Roo.EventObject} e
35608          */
35609          "cellcontextmenu" : true,
35610         /**
35611          * @event headercontextmenu
35612          * Fires when a header is right clicked
35613          * @param {Grid} this
35614          * @param {Number} columnIndex
35615          * @param {Roo.EventObject} e
35616          */
35617         "headercontextmenu" : true,
35618         /**
35619          * @event bodyscroll
35620          * Fires when the body element is scrolled
35621          * @param {Number} scrollLeft
35622          * @param {Number} scrollTop
35623          */
35624         "bodyscroll" : true,
35625         /**
35626          * @event columnresize
35627          * Fires when the user resizes a column
35628          * @param {Number} columnIndex
35629          * @param {Number} newSize
35630          */
35631         "columnresize" : true,
35632         /**
35633          * @event columnmove
35634          * Fires when the user moves a column
35635          * @param {Number} oldIndex
35636          * @param {Number} newIndex
35637          */
35638         "columnmove" : true,
35639         /**
35640          * @event startdrag
35641          * Fires when row(s) start being dragged
35642          * @param {Grid} this
35643          * @param {Roo.GridDD} dd The drag drop object
35644          * @param {event} e The raw browser event
35645          */
35646         "startdrag" : true,
35647         /**
35648          * @event enddrag
35649          * Fires when a drag operation is complete
35650          * @param {Grid} this
35651          * @param {Roo.GridDD} dd The drag drop object
35652          * @param {event} e The raw browser event
35653          */
35654         "enddrag" : true,
35655         /**
35656          * @event dragdrop
35657          * Fires when dragged row(s) are dropped on a valid DD target
35658          * @param {Grid} this
35659          * @param {Roo.GridDD} dd The drag drop object
35660          * @param {String} targetId The target drag drop object
35661          * @param {event} e The raw browser event
35662          */
35663         "dragdrop" : true,
35664         /**
35665          * @event dragover
35666          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35667          * @param {Grid} this
35668          * @param {Roo.GridDD} dd The drag drop object
35669          * @param {String} targetId The target drag drop object
35670          * @param {event} e The raw browser event
35671          */
35672         "dragover" : true,
35673         /**
35674          * @event dragenter
35675          *  Fires when the dragged row(s) first cross another DD target while being dragged
35676          * @param {Grid} this
35677          * @param {Roo.GridDD} dd The drag drop object
35678          * @param {String} targetId The target drag drop object
35679          * @param {event} e The raw browser event
35680          */
35681         "dragenter" : true,
35682         /**
35683          * @event dragout
35684          * Fires when the dragged row(s) leave another DD target while being dragged
35685          * @param {Grid} this
35686          * @param {Roo.GridDD} dd The drag drop object
35687          * @param {String} targetId The target drag drop object
35688          * @param {event} e The raw browser event
35689          */
35690         "dragout" : true,
35691         /**
35692          * @event rowclass
35693          * Fires when a row is rendered, so you can change add a style to it.
35694          * @param {GridView} gridview   The grid view
35695          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35696          */
35697         'rowclass' : true,
35698
35699         /**
35700          * @event render
35701          * Fires when the grid is rendered
35702          * @param {Grid} grid
35703          */
35704         'render' : true
35705     });
35706
35707     Roo.grid.Grid.superclass.constructor.call(this);
35708 };
35709 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35710     
35711     /**
35712      * @cfg {String} ddGroup - drag drop group.
35713      */
35714
35715     /**
35716      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35717      */
35718     minColumnWidth : 25,
35719
35720     /**
35721      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35722      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35723      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35724      */
35725     autoSizeColumns : false,
35726
35727     /**
35728      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35729      */
35730     autoSizeHeaders : true,
35731
35732     /**
35733      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35734      */
35735     monitorWindowResize : true,
35736
35737     /**
35738      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35739      * rows measured to get a columns size. Default is 0 (all rows).
35740      */
35741     maxRowsToMeasure : 0,
35742
35743     /**
35744      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35745      */
35746     trackMouseOver : true,
35747
35748     /**
35749     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35750     */
35751     
35752     /**
35753     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35754     */
35755     enableDragDrop : false,
35756     
35757     /**
35758     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35759     */
35760     enableColumnMove : true,
35761     
35762     /**
35763     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35764     */
35765     enableColumnHide : true,
35766     
35767     /**
35768     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35769     */
35770     enableRowHeightSync : false,
35771     
35772     /**
35773     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35774     */
35775     stripeRows : true,
35776     
35777     /**
35778     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35779     */
35780     autoHeight : false,
35781
35782     /**
35783      * @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.
35784      */
35785     autoExpandColumn : false,
35786
35787     /**
35788     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35789     * Default is 50.
35790     */
35791     autoExpandMin : 50,
35792
35793     /**
35794     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35795     */
35796     autoExpandMax : 1000,
35797
35798     /**
35799     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35800     */
35801     view : null,
35802
35803     /**
35804     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35805     */
35806     loadMask : false,
35807     /**
35808     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35809     */
35810     dropTarget: false,
35811     
35812    
35813     
35814     // private
35815     rendered : false,
35816
35817     /**
35818     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35819     * of a fixed width. Default is false.
35820     */
35821     /**
35822     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35823     */
35824     /**
35825      * Called once after all setup has been completed and the grid is ready to be rendered.
35826      * @return {Roo.grid.Grid} this
35827      */
35828     render : function()
35829     {
35830         var c = this.container;
35831         // try to detect autoHeight/width mode
35832         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35833             this.autoHeight = true;
35834         }
35835         var view = this.getView();
35836         view.init(this);
35837
35838         c.on("click", this.onClick, this);
35839         c.on("dblclick", this.onDblClick, this);
35840         c.on("contextmenu", this.onContextMenu, this);
35841         c.on("keydown", this.onKeyDown, this);
35842         if (Roo.isTouch) {
35843             c.on("touchstart", this.onTouchStart, this);
35844         }
35845
35846         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35847
35848         this.getSelectionModel().init(this);
35849
35850         view.render();
35851
35852         if(this.loadMask){
35853             this.loadMask = new Roo.LoadMask(this.container,
35854                     Roo.apply({store:this.dataSource}, this.loadMask));
35855         }
35856         
35857         
35858         if (this.toolbar && this.toolbar.xtype) {
35859             this.toolbar.container = this.getView().getHeaderPanel(true);
35860             this.toolbar = new Roo.Toolbar(this.toolbar);
35861         }
35862         if (this.footer && this.footer.xtype) {
35863             this.footer.dataSource = this.getDataSource();
35864             this.footer.container = this.getView().getFooterPanel(true);
35865             this.footer = Roo.factory(this.footer, Roo);
35866         }
35867         if (this.dropTarget && this.dropTarget.xtype) {
35868             delete this.dropTarget.xtype;
35869             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35870         }
35871         
35872         
35873         this.rendered = true;
35874         this.fireEvent('render', this);
35875         return this;
35876     },
35877
35878         /**
35879          * Reconfigures the grid to use a different Store and Column Model.
35880          * The View will be bound to the new objects and refreshed.
35881          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35882          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35883          */
35884     reconfigure : function(dataSource, colModel){
35885         if(this.loadMask){
35886             this.loadMask.destroy();
35887             this.loadMask = new Roo.LoadMask(this.container,
35888                     Roo.apply({store:dataSource}, this.loadMask));
35889         }
35890         this.view.bind(dataSource, colModel);
35891         this.dataSource = dataSource;
35892         this.colModel = colModel;
35893         this.view.refresh(true);
35894     },
35895
35896     // private
35897     onKeyDown : function(e){
35898         this.fireEvent("keydown", e);
35899     },
35900
35901     /**
35902      * Destroy this grid.
35903      * @param {Boolean} removeEl True to remove the element
35904      */
35905     destroy : function(removeEl, keepListeners){
35906         if(this.loadMask){
35907             this.loadMask.destroy();
35908         }
35909         var c = this.container;
35910         c.removeAllListeners();
35911         this.view.destroy();
35912         this.colModel.purgeListeners();
35913         if(!keepListeners){
35914             this.purgeListeners();
35915         }
35916         c.update("");
35917         if(removeEl === true){
35918             c.remove();
35919         }
35920     },
35921
35922     // private
35923     processEvent : function(name, e){
35924         // does this fire select???
35925         Roo.log('grid:processEvent '  + name);
35926         
35927         if (name != 'touchstart' ) {
35928             this.fireEvent(name, e);    
35929         }
35930         
35931         var t = e.getTarget();
35932         var v = this.view;
35933         var header = v.findHeaderIndex(t);
35934         if(header !== false){
35935             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35936         }else{
35937             var row = v.findRowIndex(t);
35938             var cell = v.findCellIndex(t);
35939             if (name == 'touchstart') {
35940                 // first touch is always a click.
35941                 // hopefull this happens after selection is updated.?
35942                 name = false;
35943                 
35944                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35945                     var cs = this.selModel.getSelectedCell();
35946                     if (row == cs[0] && cell == cs[1]){
35947                         name = 'dblclick';
35948                     }
35949                 }
35950                 if (typeof(this.selModel.getSelections) != 'undefined') {
35951                     var cs = this.selModel.getSelections();
35952                     var ds = this.dataSource;
35953                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35954                         name = 'dblclick';
35955                     }
35956                 }
35957                 if (!name) {
35958                     return;
35959                 }
35960             }
35961             
35962             
35963             if(row !== false){
35964                 this.fireEvent("row" + name, this, row, e);
35965                 if(cell !== false){
35966                     this.fireEvent("cell" + name, this, row, cell, e);
35967                 }
35968             }
35969         }
35970     },
35971
35972     // private
35973     onClick : function(e){
35974         this.processEvent("click", e);
35975     },
35976    // private
35977     onTouchStart : function(e){
35978         this.processEvent("touchstart", e);
35979     },
35980
35981     // private
35982     onContextMenu : function(e, t){
35983         this.processEvent("contextmenu", e);
35984     },
35985
35986     // private
35987     onDblClick : function(e){
35988         this.processEvent("dblclick", e);
35989     },
35990
35991     // private
35992     walkCells : function(row, col, step, fn, scope){
35993         var cm = this.colModel, clen = cm.getColumnCount();
35994         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35995         if(step < 0){
35996             if(col < 0){
35997                 row--;
35998                 first = false;
35999             }
36000             while(row >= 0){
36001                 if(!first){
36002                     col = clen-1;
36003                 }
36004                 first = false;
36005                 while(col >= 0){
36006                     if(fn.call(scope || this, row, col, cm) === true){
36007                         return [row, col];
36008                     }
36009                     col--;
36010                 }
36011                 row--;
36012             }
36013         } else {
36014             if(col >= clen){
36015                 row++;
36016                 first = false;
36017             }
36018             while(row < rlen){
36019                 if(!first){
36020                     col = 0;
36021                 }
36022                 first = false;
36023                 while(col < clen){
36024                     if(fn.call(scope || this, row, col, cm) === true){
36025                         return [row, col];
36026                     }
36027                     col++;
36028                 }
36029                 row++;
36030             }
36031         }
36032         return null;
36033     },
36034
36035     // private
36036     getSelections : function(){
36037         return this.selModel.getSelections();
36038     },
36039
36040     /**
36041      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36042      * but if manual update is required this method will initiate it.
36043      */
36044     autoSize : function(){
36045         if(this.rendered){
36046             this.view.layout();
36047             if(this.view.adjustForScroll){
36048                 this.view.adjustForScroll();
36049             }
36050         }
36051     },
36052
36053     /**
36054      * Returns the grid's underlying element.
36055      * @return {Element} The element
36056      */
36057     getGridEl : function(){
36058         return this.container;
36059     },
36060
36061     // private for compatibility, overridden by editor grid
36062     stopEditing : function(){},
36063
36064     /**
36065      * Returns the grid's SelectionModel.
36066      * @return {SelectionModel}
36067      */
36068     getSelectionModel : function(){
36069         if(!this.selModel){
36070             this.selModel = new Roo.grid.RowSelectionModel();
36071         }
36072         return this.selModel;
36073     },
36074
36075     /**
36076      * Returns the grid's DataSource.
36077      * @return {DataSource}
36078      */
36079     getDataSource : function(){
36080         return this.dataSource;
36081     },
36082
36083     /**
36084      * Returns the grid's ColumnModel.
36085      * @return {ColumnModel}
36086      */
36087     getColumnModel : function(){
36088         return this.colModel;
36089     },
36090
36091     /**
36092      * Returns the grid's GridView object.
36093      * @return {GridView}
36094      */
36095     getView : function(){
36096         if(!this.view){
36097             this.view = new Roo.grid.GridView(this.viewConfig);
36098         }
36099         return this.view;
36100     },
36101     /**
36102      * Called to get grid's drag proxy text, by default returns this.ddText.
36103      * @return {String}
36104      */
36105     getDragDropText : function(){
36106         var count = this.selModel.getCount();
36107         return String.format(this.ddText, count, count == 1 ? '' : 's');
36108     }
36109 });
36110 /**
36111  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36112  * %0 is replaced with the number of selected rows.
36113  * @type String
36114  */
36115 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36116  * Based on:
36117  * Ext JS Library 1.1.1
36118  * Copyright(c) 2006-2007, Ext JS, LLC.
36119  *
36120  * Originally Released Under LGPL - original licence link has changed is not relivant.
36121  *
36122  * Fork - LGPL
36123  * <script type="text/javascript">
36124  */
36125  
36126 Roo.grid.AbstractGridView = function(){
36127         this.grid = null;
36128         
36129         this.events = {
36130             "beforerowremoved" : true,
36131             "beforerowsinserted" : true,
36132             "beforerefresh" : true,
36133             "rowremoved" : true,
36134             "rowsinserted" : true,
36135             "rowupdated" : true,
36136             "refresh" : true
36137         };
36138     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36139 };
36140
36141 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36142     rowClass : "x-grid-row",
36143     cellClass : "x-grid-cell",
36144     tdClass : "x-grid-td",
36145     hdClass : "x-grid-hd",
36146     splitClass : "x-grid-hd-split",
36147     
36148         init: function(grid){
36149         this.grid = grid;
36150                 var cid = this.grid.getGridEl().id;
36151         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36152         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36153         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36154         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36155         },
36156         
36157         getColumnRenderers : function(){
36158         var renderers = [];
36159         var cm = this.grid.colModel;
36160         var colCount = cm.getColumnCount();
36161         for(var i = 0; i < colCount; i++){
36162             renderers[i] = cm.getRenderer(i);
36163         }
36164         return renderers;
36165     },
36166     
36167     getColumnIds : function(){
36168         var ids = [];
36169         var cm = this.grid.colModel;
36170         var colCount = cm.getColumnCount();
36171         for(var i = 0; i < colCount; i++){
36172             ids[i] = cm.getColumnId(i);
36173         }
36174         return ids;
36175     },
36176     
36177     getDataIndexes : function(){
36178         if(!this.indexMap){
36179             this.indexMap = this.buildIndexMap();
36180         }
36181         return this.indexMap.colToData;
36182     },
36183     
36184     getColumnIndexByDataIndex : function(dataIndex){
36185         if(!this.indexMap){
36186             this.indexMap = this.buildIndexMap();
36187         }
36188         return this.indexMap.dataToCol[dataIndex];
36189     },
36190     
36191     /**
36192      * Set a css style for a column dynamically. 
36193      * @param {Number} colIndex The index of the column
36194      * @param {String} name The css property name
36195      * @param {String} value The css value
36196      */
36197     setCSSStyle : function(colIndex, name, value){
36198         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36199         Roo.util.CSS.updateRule(selector, name, value);
36200     },
36201     
36202     generateRules : function(cm){
36203         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36204         Roo.util.CSS.removeStyleSheet(rulesId);
36205         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36206             var cid = cm.getColumnId(i);
36207             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36208                          this.tdSelector, cid, " {\n}\n",
36209                          this.hdSelector, cid, " {\n}\n",
36210                          this.splitSelector, cid, " {\n}\n");
36211         }
36212         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36213     }
36214 });/*
36215  * Based on:
36216  * Ext JS Library 1.1.1
36217  * Copyright(c) 2006-2007, Ext JS, LLC.
36218  *
36219  * Originally Released Under LGPL - original licence link has changed is not relivant.
36220  *
36221  * Fork - LGPL
36222  * <script type="text/javascript">
36223  */
36224
36225 // private
36226 // This is a support class used internally by the Grid components
36227 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36228     this.grid = grid;
36229     this.view = grid.getView();
36230     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36231     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36232     if(hd2){
36233         this.setHandleElId(Roo.id(hd));
36234         this.setOuterHandleElId(Roo.id(hd2));
36235     }
36236     this.scroll = false;
36237 };
36238 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36239     maxDragWidth: 120,
36240     getDragData : function(e){
36241         var t = Roo.lib.Event.getTarget(e);
36242         var h = this.view.findHeaderCell(t);
36243         if(h){
36244             return {ddel: h.firstChild, header:h};
36245         }
36246         return false;
36247     },
36248
36249     onInitDrag : function(e){
36250         this.view.headersDisabled = true;
36251         var clone = this.dragData.ddel.cloneNode(true);
36252         clone.id = Roo.id();
36253         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36254         this.proxy.update(clone);
36255         return true;
36256     },
36257
36258     afterValidDrop : function(){
36259         var v = this.view;
36260         setTimeout(function(){
36261             v.headersDisabled = false;
36262         }, 50);
36263     },
36264
36265     afterInvalidDrop : function(){
36266         var v = this.view;
36267         setTimeout(function(){
36268             v.headersDisabled = false;
36269         }, 50);
36270     }
36271 });
36272 /*
36273  * Based on:
36274  * Ext JS Library 1.1.1
36275  * Copyright(c) 2006-2007, Ext JS, LLC.
36276  *
36277  * Originally Released Under LGPL - original licence link has changed is not relivant.
36278  *
36279  * Fork - LGPL
36280  * <script type="text/javascript">
36281  */
36282 // private
36283 // This is a support class used internally by the Grid components
36284 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36285     this.grid = grid;
36286     this.view = grid.getView();
36287     // split the proxies so they don't interfere with mouse events
36288     this.proxyTop = Roo.DomHelper.append(document.body, {
36289         cls:"col-move-top", html:"&#160;"
36290     }, true);
36291     this.proxyBottom = Roo.DomHelper.append(document.body, {
36292         cls:"col-move-bottom", html:"&#160;"
36293     }, true);
36294     this.proxyTop.hide = this.proxyBottom.hide = function(){
36295         this.setLeftTop(-100,-100);
36296         this.setStyle("visibility", "hidden");
36297     };
36298     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36299     // temporarily disabled
36300     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36301     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36302 };
36303 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36304     proxyOffsets : [-4, -9],
36305     fly: Roo.Element.fly,
36306
36307     getTargetFromEvent : function(e){
36308         var t = Roo.lib.Event.getTarget(e);
36309         var cindex = this.view.findCellIndex(t);
36310         if(cindex !== false){
36311             return this.view.getHeaderCell(cindex);
36312         }
36313         return null;
36314     },
36315
36316     nextVisible : function(h){
36317         var v = this.view, cm = this.grid.colModel;
36318         h = h.nextSibling;
36319         while(h){
36320             if(!cm.isHidden(v.getCellIndex(h))){
36321                 return h;
36322             }
36323             h = h.nextSibling;
36324         }
36325         return null;
36326     },
36327
36328     prevVisible : function(h){
36329         var v = this.view, cm = this.grid.colModel;
36330         h = h.prevSibling;
36331         while(h){
36332             if(!cm.isHidden(v.getCellIndex(h))){
36333                 return h;
36334             }
36335             h = h.prevSibling;
36336         }
36337         return null;
36338     },
36339
36340     positionIndicator : function(h, n, e){
36341         var x = Roo.lib.Event.getPageX(e);
36342         var r = Roo.lib.Dom.getRegion(n.firstChild);
36343         var px, pt, py = r.top + this.proxyOffsets[1];
36344         if((r.right - x) <= (r.right-r.left)/2){
36345             px = r.right+this.view.borderWidth;
36346             pt = "after";
36347         }else{
36348             px = r.left;
36349             pt = "before";
36350         }
36351         var oldIndex = this.view.getCellIndex(h);
36352         var newIndex = this.view.getCellIndex(n);
36353
36354         if(this.grid.colModel.isFixed(newIndex)){
36355             return false;
36356         }
36357
36358         var locked = this.grid.colModel.isLocked(newIndex);
36359
36360         if(pt == "after"){
36361             newIndex++;
36362         }
36363         if(oldIndex < newIndex){
36364             newIndex--;
36365         }
36366         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36367             return false;
36368         }
36369         px +=  this.proxyOffsets[0];
36370         this.proxyTop.setLeftTop(px, py);
36371         this.proxyTop.show();
36372         if(!this.bottomOffset){
36373             this.bottomOffset = this.view.mainHd.getHeight();
36374         }
36375         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36376         this.proxyBottom.show();
36377         return pt;
36378     },
36379
36380     onNodeEnter : function(n, dd, e, data){
36381         if(data.header != n){
36382             this.positionIndicator(data.header, n, e);
36383         }
36384     },
36385
36386     onNodeOver : function(n, dd, e, data){
36387         var result = false;
36388         if(data.header != n){
36389             result = this.positionIndicator(data.header, n, e);
36390         }
36391         if(!result){
36392             this.proxyTop.hide();
36393             this.proxyBottom.hide();
36394         }
36395         return result ? this.dropAllowed : this.dropNotAllowed;
36396     },
36397
36398     onNodeOut : function(n, dd, e, data){
36399         this.proxyTop.hide();
36400         this.proxyBottom.hide();
36401     },
36402
36403     onNodeDrop : function(n, dd, e, data){
36404         var h = data.header;
36405         if(h != n){
36406             var cm = this.grid.colModel;
36407             var x = Roo.lib.Event.getPageX(e);
36408             var r = Roo.lib.Dom.getRegion(n.firstChild);
36409             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36410             var oldIndex = this.view.getCellIndex(h);
36411             var newIndex = this.view.getCellIndex(n);
36412             var locked = cm.isLocked(newIndex);
36413             if(pt == "after"){
36414                 newIndex++;
36415             }
36416             if(oldIndex < newIndex){
36417                 newIndex--;
36418             }
36419             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36420                 return false;
36421             }
36422             cm.setLocked(oldIndex, locked, true);
36423             cm.moveColumn(oldIndex, newIndex);
36424             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36425             return true;
36426         }
36427         return false;
36428     }
36429 });
36430 /*
36431  * Based on:
36432  * Ext JS Library 1.1.1
36433  * Copyright(c) 2006-2007, Ext JS, LLC.
36434  *
36435  * Originally Released Under LGPL - original licence link has changed is not relivant.
36436  *
36437  * Fork - LGPL
36438  * <script type="text/javascript">
36439  */
36440   
36441 /**
36442  * @class Roo.grid.GridView
36443  * @extends Roo.util.Observable
36444  *
36445  * @constructor
36446  * @param {Object} config
36447  */
36448 Roo.grid.GridView = function(config){
36449     Roo.grid.GridView.superclass.constructor.call(this);
36450     this.el = null;
36451
36452     Roo.apply(this, config);
36453 };
36454
36455 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36456
36457     unselectable :  'unselectable="on"',
36458     unselectableCls :  'x-unselectable',
36459     
36460     
36461     rowClass : "x-grid-row",
36462
36463     cellClass : "x-grid-col",
36464
36465     tdClass : "x-grid-td",
36466
36467     hdClass : "x-grid-hd",
36468
36469     splitClass : "x-grid-split",
36470
36471     sortClasses : ["sort-asc", "sort-desc"],
36472
36473     enableMoveAnim : false,
36474
36475     hlColor: "C3DAF9",
36476
36477     dh : Roo.DomHelper,
36478
36479     fly : Roo.Element.fly,
36480
36481     css : Roo.util.CSS,
36482
36483     borderWidth: 1,
36484
36485     splitOffset: 3,
36486
36487     scrollIncrement : 22,
36488
36489     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36490
36491     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36492
36493     bind : function(ds, cm){
36494         if(this.ds){
36495             this.ds.un("load", this.onLoad, this);
36496             this.ds.un("datachanged", this.onDataChange, this);
36497             this.ds.un("add", this.onAdd, this);
36498             this.ds.un("remove", this.onRemove, this);
36499             this.ds.un("update", this.onUpdate, this);
36500             this.ds.un("clear", this.onClear, this);
36501         }
36502         if(ds){
36503             ds.on("load", this.onLoad, this);
36504             ds.on("datachanged", this.onDataChange, this);
36505             ds.on("add", this.onAdd, this);
36506             ds.on("remove", this.onRemove, this);
36507             ds.on("update", this.onUpdate, this);
36508             ds.on("clear", this.onClear, this);
36509         }
36510         this.ds = ds;
36511
36512         if(this.cm){
36513             this.cm.un("widthchange", this.onColWidthChange, this);
36514             this.cm.un("headerchange", this.onHeaderChange, this);
36515             this.cm.un("hiddenchange", this.onHiddenChange, this);
36516             this.cm.un("columnmoved", this.onColumnMove, this);
36517             this.cm.un("columnlockchange", this.onColumnLock, this);
36518         }
36519         if(cm){
36520             this.generateRules(cm);
36521             cm.on("widthchange", this.onColWidthChange, this);
36522             cm.on("headerchange", this.onHeaderChange, this);
36523             cm.on("hiddenchange", this.onHiddenChange, this);
36524             cm.on("columnmoved", this.onColumnMove, this);
36525             cm.on("columnlockchange", this.onColumnLock, this);
36526         }
36527         this.cm = cm;
36528     },
36529
36530     init: function(grid){
36531         Roo.grid.GridView.superclass.init.call(this, grid);
36532
36533         this.bind(grid.dataSource, grid.colModel);
36534
36535         grid.on("headerclick", this.handleHeaderClick, this);
36536
36537         if(grid.trackMouseOver){
36538             grid.on("mouseover", this.onRowOver, this);
36539             grid.on("mouseout", this.onRowOut, this);
36540         }
36541         grid.cancelTextSelection = function(){};
36542         this.gridId = grid.id;
36543
36544         var tpls = this.templates || {};
36545
36546         if(!tpls.master){
36547             tpls.master = new Roo.Template(
36548                '<div class="x-grid" hidefocus="true">',
36549                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36550                   '<div class="x-grid-topbar"></div>',
36551                   '<div class="x-grid-scroller"><div></div></div>',
36552                   '<div class="x-grid-locked">',
36553                       '<div class="x-grid-header">{lockedHeader}</div>',
36554                       '<div class="x-grid-body">{lockedBody}</div>',
36555                   "</div>",
36556                   '<div class="x-grid-viewport">',
36557                       '<div class="x-grid-header">{header}</div>',
36558                       '<div class="x-grid-body">{body}</div>',
36559                   "</div>",
36560                   '<div class="x-grid-bottombar"></div>',
36561                  
36562                   '<div class="x-grid-resize-proxy">&#160;</div>',
36563                "</div>"
36564             );
36565             tpls.master.disableformats = true;
36566         }
36567
36568         if(!tpls.header){
36569             tpls.header = new Roo.Template(
36570                '<table border="0" cellspacing="0" cellpadding="0">',
36571                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36572                "</table>{splits}"
36573             );
36574             tpls.header.disableformats = true;
36575         }
36576         tpls.header.compile();
36577
36578         if(!tpls.hcell){
36579             tpls.hcell = new Roo.Template(
36580                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36581                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36582                 "</div></td>"
36583              );
36584              tpls.hcell.disableFormats = true;
36585         }
36586         tpls.hcell.compile();
36587
36588         if(!tpls.hsplit){
36589             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36590                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36591             tpls.hsplit.disableFormats = true;
36592         }
36593         tpls.hsplit.compile();
36594
36595         if(!tpls.body){
36596             tpls.body = new Roo.Template(
36597                '<table border="0" cellspacing="0" cellpadding="0">',
36598                "<tbody>{rows}</tbody>",
36599                "</table>"
36600             );
36601             tpls.body.disableFormats = true;
36602         }
36603         tpls.body.compile();
36604
36605         if(!tpls.row){
36606             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36607             tpls.row.disableFormats = true;
36608         }
36609         tpls.row.compile();
36610
36611         if(!tpls.cell){
36612             tpls.cell = new Roo.Template(
36613                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36614                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36615                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36616                 "</td>"
36617             );
36618             tpls.cell.disableFormats = true;
36619         }
36620         tpls.cell.compile();
36621
36622         this.templates = tpls;
36623     },
36624
36625     // remap these for backwards compat
36626     onColWidthChange : function(){
36627         this.updateColumns.apply(this, arguments);
36628     },
36629     onHeaderChange : function(){
36630         this.updateHeaders.apply(this, arguments);
36631     }, 
36632     onHiddenChange : function(){
36633         this.handleHiddenChange.apply(this, arguments);
36634     },
36635     onColumnMove : function(){
36636         this.handleColumnMove.apply(this, arguments);
36637     },
36638     onColumnLock : function(){
36639         this.handleLockChange.apply(this, arguments);
36640     },
36641
36642     onDataChange : function(){
36643         this.refresh();
36644         this.updateHeaderSortState();
36645     },
36646
36647     onClear : function(){
36648         this.refresh();
36649     },
36650
36651     onUpdate : function(ds, record){
36652         this.refreshRow(record);
36653     },
36654
36655     refreshRow : function(record){
36656         var ds = this.ds, index;
36657         if(typeof record == 'number'){
36658             index = record;
36659             record = ds.getAt(index);
36660         }else{
36661             index = ds.indexOf(record);
36662         }
36663         this.insertRows(ds, index, index, true);
36664         this.onRemove(ds, record, index+1, true);
36665         this.syncRowHeights(index, index);
36666         this.layout();
36667         this.fireEvent("rowupdated", this, index, record);
36668     },
36669
36670     onAdd : function(ds, records, index){
36671         this.insertRows(ds, index, index + (records.length-1));
36672     },
36673
36674     onRemove : function(ds, record, index, isUpdate){
36675         if(isUpdate !== true){
36676             this.fireEvent("beforerowremoved", this, index, record);
36677         }
36678         var bt = this.getBodyTable(), lt = this.getLockedTable();
36679         if(bt.rows[index]){
36680             bt.firstChild.removeChild(bt.rows[index]);
36681         }
36682         if(lt.rows[index]){
36683             lt.firstChild.removeChild(lt.rows[index]);
36684         }
36685         if(isUpdate !== true){
36686             this.stripeRows(index);
36687             this.syncRowHeights(index, index);
36688             this.layout();
36689             this.fireEvent("rowremoved", this, index, record);
36690         }
36691     },
36692
36693     onLoad : function(){
36694         this.scrollToTop();
36695     },
36696
36697     /**
36698      * Scrolls the grid to the top
36699      */
36700     scrollToTop : function(){
36701         if(this.scroller){
36702             this.scroller.dom.scrollTop = 0;
36703             this.syncScroll();
36704         }
36705     },
36706
36707     /**
36708      * Gets a panel in the header of the grid that can be used for toolbars etc.
36709      * After modifying the contents of this panel a call to grid.autoSize() may be
36710      * required to register any changes in size.
36711      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36712      * @return Roo.Element
36713      */
36714     getHeaderPanel : function(doShow){
36715         if(doShow){
36716             this.headerPanel.show();
36717         }
36718         return this.headerPanel;
36719     },
36720
36721     /**
36722      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36723      * After modifying the contents of this panel a call to grid.autoSize() may be
36724      * required to register any changes in size.
36725      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36726      * @return Roo.Element
36727      */
36728     getFooterPanel : function(doShow){
36729         if(doShow){
36730             this.footerPanel.show();
36731         }
36732         return this.footerPanel;
36733     },
36734
36735     initElements : function(){
36736         var E = Roo.Element;
36737         var el = this.grid.getGridEl().dom.firstChild;
36738         var cs = el.childNodes;
36739
36740         this.el = new E(el);
36741         
36742          this.focusEl = new E(el.firstChild);
36743         this.focusEl.swallowEvent("click", true);
36744         
36745         this.headerPanel = new E(cs[1]);
36746         this.headerPanel.enableDisplayMode("block");
36747
36748         this.scroller = new E(cs[2]);
36749         this.scrollSizer = new E(this.scroller.dom.firstChild);
36750
36751         this.lockedWrap = new E(cs[3]);
36752         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36753         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36754
36755         this.mainWrap = new E(cs[4]);
36756         this.mainHd = new E(this.mainWrap.dom.firstChild);
36757         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36758
36759         this.footerPanel = new E(cs[5]);
36760         this.footerPanel.enableDisplayMode("block");
36761
36762         this.resizeProxy = new E(cs[6]);
36763
36764         this.headerSelector = String.format(
36765            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36766            this.lockedHd.id, this.mainHd.id
36767         );
36768
36769         this.splitterSelector = String.format(
36770            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36771            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36772         );
36773     },
36774     idToCssName : function(s)
36775     {
36776         return s.replace(/[^a-z0-9]+/ig, '-');
36777     },
36778
36779     getHeaderCell : function(index){
36780         return Roo.DomQuery.select(this.headerSelector)[index];
36781     },
36782
36783     getHeaderCellMeasure : function(index){
36784         return this.getHeaderCell(index).firstChild;
36785     },
36786
36787     getHeaderCellText : function(index){
36788         return this.getHeaderCell(index).firstChild.firstChild;
36789     },
36790
36791     getLockedTable : function(){
36792         return this.lockedBody.dom.firstChild;
36793     },
36794
36795     getBodyTable : function(){
36796         return this.mainBody.dom.firstChild;
36797     },
36798
36799     getLockedRow : function(index){
36800         return this.getLockedTable().rows[index];
36801     },
36802
36803     getRow : function(index){
36804         return this.getBodyTable().rows[index];
36805     },
36806
36807     getRowComposite : function(index){
36808         if(!this.rowEl){
36809             this.rowEl = new Roo.CompositeElementLite();
36810         }
36811         var els = [], lrow, mrow;
36812         if(lrow = this.getLockedRow(index)){
36813             els.push(lrow);
36814         }
36815         if(mrow = this.getRow(index)){
36816             els.push(mrow);
36817         }
36818         this.rowEl.elements = els;
36819         return this.rowEl;
36820     },
36821     /**
36822      * Gets the 'td' of the cell
36823      * 
36824      * @param {Integer} rowIndex row to select
36825      * @param {Integer} colIndex column to select
36826      * 
36827      * @return {Object} 
36828      */
36829     getCell : function(rowIndex, colIndex){
36830         var locked = this.cm.getLockedCount();
36831         var source;
36832         if(colIndex < locked){
36833             source = this.lockedBody.dom.firstChild;
36834         }else{
36835             source = this.mainBody.dom.firstChild;
36836             colIndex -= locked;
36837         }
36838         return source.rows[rowIndex].childNodes[colIndex];
36839     },
36840
36841     getCellText : function(rowIndex, colIndex){
36842         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36843     },
36844
36845     getCellBox : function(cell){
36846         var b = this.fly(cell).getBox();
36847         if(Roo.isOpera){ // opera fails to report the Y
36848             b.y = cell.offsetTop + this.mainBody.getY();
36849         }
36850         return b;
36851     },
36852
36853     getCellIndex : function(cell){
36854         var id = String(cell.className).match(this.cellRE);
36855         if(id){
36856             return parseInt(id[1], 10);
36857         }
36858         return 0;
36859     },
36860
36861     findHeaderIndex : function(n){
36862         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36863         return r ? this.getCellIndex(r) : false;
36864     },
36865
36866     findHeaderCell : function(n){
36867         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36868         return r ? r : false;
36869     },
36870
36871     findRowIndex : function(n){
36872         if(!n){
36873             return false;
36874         }
36875         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36876         return r ? r.rowIndex : false;
36877     },
36878
36879     findCellIndex : function(node){
36880         var stop = this.el.dom;
36881         while(node && node != stop){
36882             if(this.findRE.test(node.className)){
36883                 return this.getCellIndex(node);
36884             }
36885             node = node.parentNode;
36886         }
36887         return false;
36888     },
36889
36890     getColumnId : function(index){
36891         return this.cm.getColumnId(index);
36892     },
36893
36894     getSplitters : function()
36895     {
36896         if(this.splitterSelector){
36897            return Roo.DomQuery.select(this.splitterSelector);
36898         }else{
36899             return null;
36900       }
36901     },
36902
36903     getSplitter : function(index){
36904         return this.getSplitters()[index];
36905     },
36906
36907     onRowOver : function(e, t){
36908         var row;
36909         if((row = this.findRowIndex(t)) !== false){
36910             this.getRowComposite(row).addClass("x-grid-row-over");
36911         }
36912     },
36913
36914     onRowOut : function(e, t){
36915         var row;
36916         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36917             this.getRowComposite(row).removeClass("x-grid-row-over");
36918         }
36919     },
36920
36921     renderHeaders : function(){
36922         var cm = this.cm;
36923         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36924         var cb = [], lb = [], sb = [], lsb = [], p = {};
36925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36926             p.cellId = "x-grid-hd-0-" + i;
36927             p.splitId = "x-grid-csplit-0-" + i;
36928             p.id = cm.getColumnId(i);
36929             p.title = cm.getColumnTooltip(i) || "";
36930             p.value = cm.getColumnHeader(i) || "";
36931             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36932             if(!cm.isLocked(i)){
36933                 cb[cb.length] = ct.apply(p);
36934                 sb[sb.length] = st.apply(p);
36935             }else{
36936                 lb[lb.length] = ct.apply(p);
36937                 lsb[lsb.length] = st.apply(p);
36938             }
36939         }
36940         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36941                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36942     },
36943
36944     updateHeaders : function(){
36945         var html = this.renderHeaders();
36946         this.lockedHd.update(html[0]);
36947         this.mainHd.update(html[1]);
36948     },
36949
36950     /**
36951      * Focuses the specified row.
36952      * @param {Number} row The row index
36953      */
36954     focusRow : function(row)
36955     {
36956         //Roo.log('GridView.focusRow');
36957         var x = this.scroller.dom.scrollLeft;
36958         this.focusCell(row, 0, false);
36959         this.scroller.dom.scrollLeft = x;
36960     },
36961
36962     /**
36963      * Focuses the specified cell.
36964      * @param {Number} row The row index
36965      * @param {Number} col The column index
36966      * @param {Boolean} hscroll false to disable horizontal scrolling
36967      */
36968     focusCell : function(row, col, hscroll)
36969     {
36970         //Roo.log('GridView.focusCell');
36971         var el = this.ensureVisible(row, col, hscroll);
36972         this.focusEl.alignTo(el, "tl-tl");
36973         if(Roo.isGecko){
36974             this.focusEl.focus();
36975         }else{
36976             this.focusEl.focus.defer(1, this.focusEl);
36977         }
36978     },
36979
36980     /**
36981      * Scrolls the specified cell into view
36982      * @param {Number} row The row index
36983      * @param {Number} col The column index
36984      * @param {Boolean} hscroll false to disable horizontal scrolling
36985      */
36986     ensureVisible : function(row, col, hscroll)
36987     {
36988         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36989         //return null; //disable for testing.
36990         if(typeof row != "number"){
36991             row = row.rowIndex;
36992         }
36993         if(row < 0 && row >= this.ds.getCount()){
36994             return  null;
36995         }
36996         col = (col !== undefined ? col : 0);
36997         var cm = this.grid.colModel;
36998         while(cm.isHidden(col)){
36999             col++;
37000         }
37001
37002         var el = this.getCell(row, col);
37003         if(!el){
37004             return null;
37005         }
37006         var c = this.scroller.dom;
37007
37008         var ctop = parseInt(el.offsetTop, 10);
37009         var cleft = parseInt(el.offsetLeft, 10);
37010         var cbot = ctop + el.offsetHeight;
37011         var cright = cleft + el.offsetWidth;
37012         
37013         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37014         var stop = parseInt(c.scrollTop, 10);
37015         var sleft = parseInt(c.scrollLeft, 10);
37016         var sbot = stop + ch;
37017         var sright = sleft + c.clientWidth;
37018         /*
37019         Roo.log('GridView.ensureVisible:' +
37020                 ' ctop:' + ctop +
37021                 ' c.clientHeight:' + c.clientHeight +
37022                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37023                 ' stop:' + stop +
37024                 ' cbot:' + cbot +
37025                 ' sbot:' + sbot +
37026                 ' ch:' + ch  
37027                 );
37028         */
37029         if(ctop < stop){
37030              c.scrollTop = ctop;
37031             //Roo.log("set scrolltop to ctop DISABLE?");
37032         }else if(cbot > sbot){
37033             //Roo.log("set scrolltop to cbot-ch");
37034             c.scrollTop = cbot-ch;
37035         }
37036         
37037         if(hscroll !== false){
37038             if(cleft < sleft){
37039                 c.scrollLeft = cleft;
37040             }else if(cright > sright){
37041                 c.scrollLeft = cright-c.clientWidth;
37042             }
37043         }
37044          
37045         return el;
37046     },
37047
37048     updateColumns : function(){
37049         this.grid.stopEditing();
37050         var cm = this.grid.colModel, colIds = this.getColumnIds();
37051         //var totalWidth = cm.getTotalWidth();
37052         var pos = 0;
37053         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37054             //if(cm.isHidden(i)) continue;
37055             var w = cm.getColumnWidth(i);
37056             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37057             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37058         }
37059         this.updateSplitters();
37060     },
37061
37062     generateRules : function(cm){
37063         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37064         Roo.util.CSS.removeStyleSheet(rulesId);
37065         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37066             var cid = cm.getColumnId(i);
37067             var align = '';
37068             if(cm.config[i].align){
37069                 align = 'text-align:'+cm.config[i].align+';';
37070             }
37071             var hidden = '';
37072             if(cm.isHidden(i)){
37073                 hidden = 'display:none;';
37074             }
37075             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37076             ruleBuf.push(
37077                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37078                     this.hdSelector, cid, " {\n", align, width, "}\n",
37079                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37080                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37081         }
37082         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37083     },
37084
37085     updateSplitters : function(){
37086         var cm = this.cm, s = this.getSplitters();
37087         if(s){ // splitters not created yet
37088             var pos = 0, locked = true;
37089             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37090                 if(cm.isHidden(i)) continue;
37091                 var w = cm.getColumnWidth(i); // make sure it's a number
37092                 if(!cm.isLocked(i) && locked){
37093                     pos = 0;
37094                     locked = false;
37095                 }
37096                 pos += w;
37097                 s[i].style.left = (pos-this.splitOffset) + "px";
37098             }
37099         }
37100     },
37101
37102     handleHiddenChange : function(colModel, colIndex, hidden){
37103         if(hidden){
37104             this.hideColumn(colIndex);
37105         }else{
37106             this.unhideColumn(colIndex);
37107         }
37108     },
37109
37110     hideColumn : function(colIndex){
37111         var cid = this.getColumnId(colIndex);
37112         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37113         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37114         if(Roo.isSafari){
37115             this.updateHeaders();
37116         }
37117         this.updateSplitters();
37118         this.layout();
37119     },
37120
37121     unhideColumn : function(colIndex){
37122         var cid = this.getColumnId(colIndex);
37123         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37124         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37125
37126         if(Roo.isSafari){
37127             this.updateHeaders();
37128         }
37129         this.updateSplitters();
37130         this.layout();
37131     },
37132
37133     insertRows : function(dm, firstRow, lastRow, isUpdate){
37134         if(firstRow == 0 && lastRow == dm.getCount()-1){
37135             this.refresh();
37136         }else{
37137             if(!isUpdate){
37138                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37139             }
37140             var s = this.getScrollState();
37141             var markup = this.renderRows(firstRow, lastRow);
37142             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37143             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37144             this.restoreScroll(s);
37145             if(!isUpdate){
37146                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37147                 this.syncRowHeights(firstRow, lastRow);
37148                 this.stripeRows(firstRow);
37149                 this.layout();
37150             }
37151         }
37152     },
37153
37154     bufferRows : function(markup, target, index){
37155         var before = null, trows = target.rows, tbody = target.tBodies[0];
37156         if(index < trows.length){
37157             before = trows[index];
37158         }
37159         var b = document.createElement("div");
37160         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37161         var rows = b.firstChild.rows;
37162         for(var i = 0, len = rows.length; i < len; i++){
37163             if(before){
37164                 tbody.insertBefore(rows[0], before);
37165             }else{
37166                 tbody.appendChild(rows[0]);
37167             }
37168         }
37169         b.innerHTML = "";
37170         b = null;
37171     },
37172
37173     deleteRows : function(dm, firstRow, lastRow){
37174         if(dm.getRowCount()<1){
37175             this.fireEvent("beforerefresh", this);
37176             this.mainBody.update("");
37177             this.lockedBody.update("");
37178             this.fireEvent("refresh", this);
37179         }else{
37180             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37181             var bt = this.getBodyTable();
37182             var tbody = bt.firstChild;
37183             var rows = bt.rows;
37184             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37185                 tbody.removeChild(rows[firstRow]);
37186             }
37187             this.stripeRows(firstRow);
37188             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37189         }
37190     },
37191
37192     updateRows : function(dataSource, firstRow, lastRow){
37193         var s = this.getScrollState();
37194         this.refresh();
37195         this.restoreScroll(s);
37196     },
37197
37198     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37199         if(!noRefresh){
37200            this.refresh();
37201         }
37202         this.updateHeaderSortState();
37203     },
37204
37205     getScrollState : function(){
37206         
37207         var sb = this.scroller.dom;
37208         return {left: sb.scrollLeft, top: sb.scrollTop};
37209     },
37210
37211     stripeRows : function(startRow){
37212         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37213             return;
37214         }
37215         startRow = startRow || 0;
37216         var rows = this.getBodyTable().rows;
37217         var lrows = this.getLockedTable().rows;
37218         var cls = ' x-grid-row-alt ';
37219         for(var i = startRow, len = rows.length; i < len; i++){
37220             var row = rows[i], lrow = lrows[i];
37221             var isAlt = ((i+1) % 2 == 0);
37222             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37223             if(isAlt == hasAlt){
37224                 continue;
37225             }
37226             if(isAlt){
37227                 row.className += " x-grid-row-alt";
37228             }else{
37229                 row.className = row.className.replace("x-grid-row-alt", "");
37230             }
37231             if(lrow){
37232                 lrow.className = row.className;
37233             }
37234         }
37235     },
37236
37237     restoreScroll : function(state){
37238         //Roo.log('GridView.restoreScroll');
37239         var sb = this.scroller.dom;
37240         sb.scrollLeft = state.left;
37241         sb.scrollTop = state.top;
37242         this.syncScroll();
37243     },
37244
37245     syncScroll : function(){
37246         //Roo.log('GridView.syncScroll');
37247         var sb = this.scroller.dom;
37248         var sh = this.mainHd.dom;
37249         var bs = this.mainBody.dom;
37250         var lv = this.lockedBody.dom;
37251         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37252         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37253     },
37254
37255     handleScroll : function(e){
37256         this.syncScroll();
37257         var sb = this.scroller.dom;
37258         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37259         e.stopEvent();
37260     },
37261
37262     handleWheel : function(e){
37263         var d = e.getWheelDelta();
37264         this.scroller.dom.scrollTop -= d*22;
37265         // set this here to prevent jumpy scrolling on large tables
37266         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37267         e.stopEvent();
37268     },
37269
37270     renderRows : function(startRow, endRow){
37271         // pull in all the crap needed to render rows
37272         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37273         var colCount = cm.getColumnCount();
37274
37275         if(ds.getCount() < 1){
37276             return ["", ""];
37277         }
37278
37279         // build a map for all the columns
37280         var cs = [];
37281         for(var i = 0; i < colCount; i++){
37282             var name = cm.getDataIndex(i);
37283             cs[i] = {
37284                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37285                 renderer : cm.getRenderer(i),
37286                 id : cm.getColumnId(i),
37287                 locked : cm.isLocked(i)
37288             };
37289         }
37290
37291         startRow = startRow || 0;
37292         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37293
37294         // records to render
37295         var rs = ds.getRange(startRow, endRow);
37296
37297         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37298     },
37299
37300     // As much as I hate to duplicate code, this was branched because FireFox really hates
37301     // [].join("") on strings. The performance difference was substantial enough to
37302     // branch this function
37303     doRender : Roo.isGecko ?
37304             function(cs, rs, ds, startRow, colCount, stripe){
37305                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37306                 // buffers
37307                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37308                 
37309                 var hasListener = this.grid.hasListener('rowclass');
37310                 var rowcfg = {};
37311                 for(var j = 0, len = rs.length; j < len; j++){
37312                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37313                     for(var i = 0; i < colCount; i++){
37314                         c = cs[i];
37315                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37316                         p.id = c.id;
37317                         p.css = p.attr = "";
37318                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37319                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37320                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37321                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37322                         }
37323                         var markup = ct.apply(p);
37324                         if(!c.locked){
37325                             cb+= markup;
37326                         }else{
37327                             lcb+= markup;
37328                         }
37329                     }
37330                     var alt = [];
37331                     if(stripe && ((rowIndex+1) % 2 == 0)){
37332                         alt.push("x-grid-row-alt")
37333                     }
37334                     if(r.dirty){
37335                         alt.push(  " x-grid-dirty-row");
37336                     }
37337                     rp.cells = lcb;
37338                     if(this.getRowClass){
37339                         alt.push(this.getRowClass(r, rowIndex));
37340                     }
37341                     if (hasListener) {
37342                         rowcfg = {
37343                              
37344                             record: r,
37345                             rowIndex : rowIndex,
37346                             rowClass : ''
37347                         }
37348                         this.grid.fireEvent('rowclass', this, rowcfg);
37349                         alt.push(rowcfg.rowClass);
37350                     }
37351                     rp.alt = alt.join(" ");
37352                     lbuf+= rt.apply(rp);
37353                     rp.cells = cb;
37354                     buf+=  rt.apply(rp);
37355                 }
37356                 return [lbuf, buf];
37357             } :
37358             function(cs, rs, ds, startRow, colCount, stripe){
37359                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37360                 // buffers
37361                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37362                 var hasListener = this.grid.hasListener('rowclass');
37363  
37364                 var rowcfg = {};
37365                 for(var j = 0, len = rs.length; j < len; j++){
37366                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37367                     for(var i = 0; i < colCount; i++){
37368                         c = cs[i];
37369                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37370                         p.id = c.id;
37371                         p.css = p.attr = "";
37372                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37373                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37374                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37375                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37376                         }
37377                         
37378                         var markup = ct.apply(p);
37379                         if(!c.locked){
37380                             cb[cb.length] = markup;
37381                         }else{
37382                             lcb[lcb.length] = markup;
37383                         }
37384                     }
37385                     var alt = [];
37386                     if(stripe && ((rowIndex+1) % 2 == 0)){
37387                         alt.push( "x-grid-row-alt");
37388                     }
37389                     if(r.dirty){
37390                         alt.push(" x-grid-dirty-row");
37391                     }
37392                     rp.cells = lcb;
37393                     if(this.getRowClass){
37394                         alt.push( this.getRowClass(r, rowIndex));
37395                     }
37396                     if (hasListener) {
37397                         rowcfg = {
37398                              
37399                             record: r,
37400                             rowIndex : rowIndex,
37401                             rowClass : ''
37402                         }
37403                         this.grid.fireEvent('rowclass', this, rowcfg);
37404                         alt.push(rowcfg.rowClass);
37405                     }
37406                     rp.alt = alt.join(" ");
37407                     rp.cells = lcb.join("");
37408                     lbuf[lbuf.length] = rt.apply(rp);
37409                     rp.cells = cb.join("");
37410                     buf[buf.length] =  rt.apply(rp);
37411                 }
37412                 return [lbuf.join(""), buf.join("")];
37413             },
37414
37415     renderBody : function(){
37416         var markup = this.renderRows();
37417         var bt = this.templates.body;
37418         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37419     },
37420
37421     /**
37422      * Refreshes the grid
37423      * @param {Boolean} headersToo
37424      */
37425     refresh : function(headersToo){
37426         this.fireEvent("beforerefresh", this);
37427         this.grid.stopEditing();
37428         var result = this.renderBody();
37429         this.lockedBody.update(result[0]);
37430         this.mainBody.update(result[1]);
37431         if(headersToo === true){
37432             this.updateHeaders();
37433             this.updateColumns();
37434             this.updateSplitters();
37435             this.updateHeaderSortState();
37436         }
37437         this.syncRowHeights();
37438         this.layout();
37439         this.fireEvent("refresh", this);
37440     },
37441
37442     handleColumnMove : function(cm, oldIndex, newIndex){
37443         this.indexMap = null;
37444         var s = this.getScrollState();
37445         this.refresh(true);
37446         this.restoreScroll(s);
37447         this.afterMove(newIndex);
37448     },
37449
37450     afterMove : function(colIndex){
37451         if(this.enableMoveAnim && Roo.enableFx){
37452             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37453         }
37454         // if multisort - fix sortOrder, and reload..
37455         if (this.grid.dataSource.multiSort) {
37456             // the we can call sort again..
37457             var dm = this.grid.dataSource;
37458             var cm = this.grid.colModel;
37459             var so = [];
37460             for(var i = 0; i < cm.config.length; i++ ) {
37461                 
37462                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37463                     continue; // dont' bother, it's not in sort list or being set.
37464                 }
37465                 
37466                 so.push(cm.config[i].dataIndex);
37467             };
37468             dm.sortOrder = so;
37469             dm.load(dm.lastOptions);
37470             
37471             
37472         }
37473         
37474     },
37475
37476     updateCell : function(dm, rowIndex, dataIndex){
37477         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37478         if(typeof colIndex == "undefined"){ // not present in grid
37479             return;
37480         }
37481         var cm = this.grid.colModel;
37482         var cell = this.getCell(rowIndex, colIndex);
37483         var cellText = this.getCellText(rowIndex, colIndex);
37484
37485         var p = {
37486             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37487             id : cm.getColumnId(colIndex),
37488             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37489         };
37490         var renderer = cm.getRenderer(colIndex);
37491         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37492         if(typeof val == "undefined" || val === "") val = "&#160;";
37493         cellText.innerHTML = val;
37494         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37495         this.syncRowHeights(rowIndex, rowIndex);
37496     },
37497
37498     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37499         var maxWidth = 0;
37500         if(this.grid.autoSizeHeaders){
37501             var h = this.getHeaderCellMeasure(colIndex);
37502             maxWidth = Math.max(maxWidth, h.scrollWidth);
37503         }
37504         var tb, index;
37505         if(this.cm.isLocked(colIndex)){
37506             tb = this.getLockedTable();
37507             index = colIndex;
37508         }else{
37509             tb = this.getBodyTable();
37510             index = colIndex - this.cm.getLockedCount();
37511         }
37512         if(tb && tb.rows){
37513             var rows = tb.rows;
37514             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37515             for(var i = 0; i < stopIndex; i++){
37516                 var cell = rows[i].childNodes[index].firstChild;
37517                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37518             }
37519         }
37520         return maxWidth + /*margin for error in IE*/ 5;
37521     },
37522     /**
37523      * Autofit a column to its content.
37524      * @param {Number} colIndex
37525      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37526      */
37527      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37528          if(this.cm.isHidden(colIndex)){
37529              return; // can't calc a hidden column
37530          }
37531         if(forceMinSize){
37532             var cid = this.cm.getColumnId(colIndex);
37533             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37534            if(this.grid.autoSizeHeaders){
37535                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37536            }
37537         }
37538         var newWidth = this.calcColumnWidth(colIndex);
37539         this.cm.setColumnWidth(colIndex,
37540             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37541         if(!suppressEvent){
37542             this.grid.fireEvent("columnresize", colIndex, newWidth);
37543         }
37544     },
37545
37546     /**
37547      * Autofits all columns to their content and then expands to fit any extra space in the grid
37548      */
37549      autoSizeColumns : function(){
37550         var cm = this.grid.colModel;
37551         var colCount = cm.getColumnCount();
37552         for(var i = 0; i < colCount; i++){
37553             this.autoSizeColumn(i, true, true);
37554         }
37555         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37556             this.fitColumns();
37557         }else{
37558             this.updateColumns();
37559             this.layout();
37560         }
37561     },
37562
37563     /**
37564      * Autofits all columns to the grid's width proportionate with their current size
37565      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37566      */
37567     fitColumns : function(reserveScrollSpace){
37568         var cm = this.grid.colModel;
37569         var colCount = cm.getColumnCount();
37570         var cols = [];
37571         var width = 0;
37572         var i, w;
37573         for (i = 0; i < colCount; i++){
37574             if(!cm.isHidden(i) && !cm.isFixed(i)){
37575                 w = cm.getColumnWidth(i);
37576                 cols.push(i);
37577                 cols.push(w);
37578                 width += w;
37579             }
37580         }
37581         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37582         if(reserveScrollSpace){
37583             avail -= 17;
37584         }
37585         var frac = (avail - cm.getTotalWidth())/width;
37586         while (cols.length){
37587             w = cols.pop();
37588             i = cols.pop();
37589             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37590         }
37591         this.updateColumns();
37592         this.layout();
37593     },
37594
37595     onRowSelect : function(rowIndex){
37596         var row = this.getRowComposite(rowIndex);
37597         row.addClass("x-grid-row-selected");
37598     },
37599
37600     onRowDeselect : function(rowIndex){
37601         var row = this.getRowComposite(rowIndex);
37602         row.removeClass("x-grid-row-selected");
37603     },
37604
37605     onCellSelect : function(row, col){
37606         var cell = this.getCell(row, col);
37607         if(cell){
37608             Roo.fly(cell).addClass("x-grid-cell-selected");
37609         }
37610     },
37611
37612     onCellDeselect : function(row, col){
37613         var cell = this.getCell(row, col);
37614         if(cell){
37615             Roo.fly(cell).removeClass("x-grid-cell-selected");
37616         }
37617     },
37618
37619     updateHeaderSortState : function(){
37620         
37621         // sort state can be single { field: xxx, direction : yyy}
37622         // or   { xxx=>ASC , yyy : DESC ..... }
37623         
37624         var mstate = {};
37625         if (!this.ds.multiSort) { 
37626             var state = this.ds.getSortState();
37627             if(!state){
37628                 return;
37629             }
37630             mstate[state.field] = state.direction;
37631             // FIXME... - this is not used here.. but might be elsewhere..
37632             this.sortState = state;
37633             
37634         } else {
37635             mstate = this.ds.sortToggle;
37636         }
37637         //remove existing sort classes..
37638         
37639         var sc = this.sortClasses;
37640         var hds = this.el.select(this.headerSelector).removeClass(sc);
37641         
37642         for(var f in mstate) {
37643         
37644             var sortColumn = this.cm.findColumnIndex(f);
37645             
37646             if(sortColumn != -1){
37647                 var sortDir = mstate[f];        
37648                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37649             }
37650         }
37651         
37652          
37653         
37654     },
37655
37656
37657     handleHeaderClick : function(g, index){
37658         if(this.headersDisabled){
37659             return;
37660         }
37661         var dm = g.dataSource, cm = g.colModel;
37662         if(!cm.isSortable(index)){
37663             return;
37664         }
37665         g.stopEditing();
37666         
37667         if (dm.multiSort) {
37668             // update the sortOrder
37669             var so = [];
37670             for(var i = 0; i < cm.config.length; i++ ) {
37671                 
37672                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37673                     continue; // dont' bother, it's not in sort list or being set.
37674                 }
37675                 
37676                 so.push(cm.config[i].dataIndex);
37677             };
37678             dm.sortOrder = so;
37679         }
37680         
37681         
37682         dm.sort(cm.getDataIndex(index));
37683     },
37684
37685
37686     destroy : function(){
37687         if(this.colMenu){
37688             this.colMenu.removeAll();
37689             Roo.menu.MenuMgr.unregister(this.colMenu);
37690             this.colMenu.getEl().remove();
37691             delete this.colMenu;
37692         }
37693         if(this.hmenu){
37694             this.hmenu.removeAll();
37695             Roo.menu.MenuMgr.unregister(this.hmenu);
37696             this.hmenu.getEl().remove();
37697             delete this.hmenu;
37698         }
37699         if(this.grid.enableColumnMove){
37700             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37701             if(dds){
37702                 for(var dd in dds){
37703                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37704                         var elid = dds[dd].dragElId;
37705                         dds[dd].unreg();
37706                         Roo.get(elid).remove();
37707                     } else if(dds[dd].config.isTarget){
37708                         dds[dd].proxyTop.remove();
37709                         dds[dd].proxyBottom.remove();
37710                         dds[dd].unreg();
37711                     }
37712                     if(Roo.dd.DDM.locationCache[dd]){
37713                         delete Roo.dd.DDM.locationCache[dd];
37714                     }
37715                 }
37716                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37717             }
37718         }
37719         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37720         this.bind(null, null);
37721         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37722     },
37723
37724     handleLockChange : function(){
37725         this.refresh(true);
37726     },
37727
37728     onDenyColumnLock : function(){
37729
37730     },
37731
37732     onDenyColumnHide : function(){
37733
37734     },
37735
37736     handleHdMenuClick : function(item){
37737         var index = this.hdCtxIndex;
37738         var cm = this.cm, ds = this.ds;
37739         switch(item.id){
37740             case "asc":
37741                 ds.sort(cm.getDataIndex(index), "ASC");
37742                 break;
37743             case "desc":
37744                 ds.sort(cm.getDataIndex(index), "DESC");
37745                 break;
37746             case "lock":
37747                 var lc = cm.getLockedCount();
37748                 if(cm.getColumnCount(true) <= lc+1){
37749                     this.onDenyColumnLock();
37750                     return;
37751                 }
37752                 if(lc != index){
37753                     cm.setLocked(index, true, true);
37754                     cm.moveColumn(index, lc);
37755                     this.grid.fireEvent("columnmove", index, lc);
37756                 }else{
37757                     cm.setLocked(index, true);
37758                 }
37759             break;
37760             case "unlock":
37761                 var lc = cm.getLockedCount();
37762                 if((lc-1) != index){
37763                     cm.setLocked(index, false, true);
37764                     cm.moveColumn(index, lc-1);
37765                     this.grid.fireEvent("columnmove", index, lc-1);
37766                 }else{
37767                     cm.setLocked(index, false);
37768                 }
37769             break;
37770             default:
37771                 index = cm.getIndexById(item.id.substr(4));
37772                 if(index != -1){
37773                     if(item.checked && cm.getColumnCount(true) <= 1){
37774                         this.onDenyColumnHide();
37775                         return false;
37776                     }
37777                     cm.setHidden(index, item.checked);
37778                 }
37779         }
37780         return true;
37781     },
37782
37783     beforeColMenuShow : function(){
37784         var cm = this.cm,  colCount = cm.getColumnCount();
37785         this.colMenu.removeAll();
37786         for(var i = 0; i < colCount; i++){
37787             this.colMenu.add(new Roo.menu.CheckItem({
37788                 id: "col-"+cm.getColumnId(i),
37789                 text: cm.getColumnHeader(i),
37790                 checked: !cm.isHidden(i),
37791                 hideOnClick:false
37792             }));
37793         }
37794     },
37795
37796     handleHdCtx : function(g, index, e){
37797         e.stopEvent();
37798         var hd = this.getHeaderCell(index);
37799         this.hdCtxIndex = index;
37800         var ms = this.hmenu.items, cm = this.cm;
37801         ms.get("asc").setDisabled(!cm.isSortable(index));
37802         ms.get("desc").setDisabled(!cm.isSortable(index));
37803         if(this.grid.enableColLock !== false){
37804             ms.get("lock").setDisabled(cm.isLocked(index));
37805             ms.get("unlock").setDisabled(!cm.isLocked(index));
37806         }
37807         this.hmenu.show(hd, "tl-bl");
37808     },
37809
37810     handleHdOver : function(e){
37811         var hd = this.findHeaderCell(e.getTarget());
37812         if(hd && !this.headersDisabled){
37813             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37814                this.fly(hd).addClass("x-grid-hd-over");
37815             }
37816         }
37817     },
37818
37819     handleHdOut : function(e){
37820         var hd = this.findHeaderCell(e.getTarget());
37821         if(hd){
37822             this.fly(hd).removeClass("x-grid-hd-over");
37823         }
37824     },
37825
37826     handleSplitDblClick : function(e, t){
37827         var i = this.getCellIndex(t);
37828         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37829             this.autoSizeColumn(i, true);
37830             this.layout();
37831         }
37832     },
37833
37834     render : function(){
37835
37836         var cm = this.cm;
37837         var colCount = cm.getColumnCount();
37838
37839         if(this.grid.monitorWindowResize === true){
37840             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37841         }
37842         var header = this.renderHeaders();
37843         var body = this.templates.body.apply({rows:""});
37844         var html = this.templates.master.apply({
37845             lockedBody: body,
37846             body: body,
37847             lockedHeader: header[0],
37848             header: header[1]
37849         });
37850
37851         //this.updateColumns();
37852
37853         this.grid.getGridEl().dom.innerHTML = html;
37854
37855         this.initElements();
37856         
37857         // a kludge to fix the random scolling effect in webkit
37858         this.el.on("scroll", function() {
37859             this.el.dom.scrollTop=0; // hopefully not recursive..
37860         },this);
37861
37862         this.scroller.on("scroll", this.handleScroll, this);
37863         this.lockedBody.on("mousewheel", this.handleWheel, this);
37864         this.mainBody.on("mousewheel", this.handleWheel, this);
37865
37866         this.mainHd.on("mouseover", this.handleHdOver, this);
37867         this.mainHd.on("mouseout", this.handleHdOut, this);
37868         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37869                 {delegate: "."+this.splitClass});
37870
37871         this.lockedHd.on("mouseover", this.handleHdOver, this);
37872         this.lockedHd.on("mouseout", this.handleHdOut, this);
37873         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37874                 {delegate: "."+this.splitClass});
37875
37876         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37877             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37878         }
37879
37880         this.updateSplitters();
37881
37882         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37883             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37884             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37885         }
37886
37887         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37888             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37889             this.hmenu.add(
37890                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37891                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37892             );
37893             if(this.grid.enableColLock !== false){
37894                 this.hmenu.add('-',
37895                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37896                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37897                 );
37898             }
37899             if(this.grid.enableColumnHide !== false){
37900
37901                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37902                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37903                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37904
37905                 this.hmenu.add('-',
37906                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37907                 );
37908             }
37909             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37910
37911             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37912         }
37913
37914         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37915             this.dd = new Roo.grid.GridDragZone(this.grid, {
37916                 ddGroup : this.grid.ddGroup || 'GridDD'
37917             });
37918             
37919         }
37920
37921         /*
37922         for(var i = 0; i < colCount; i++){
37923             if(cm.isHidden(i)){
37924                 this.hideColumn(i);
37925             }
37926             if(cm.config[i].align){
37927                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37928                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37929             }
37930         }*/
37931         
37932         this.updateHeaderSortState();
37933
37934         this.beforeInitialResize();
37935         this.layout(true);
37936
37937         // two part rendering gives faster view to the user
37938         this.renderPhase2.defer(1, this);
37939     },
37940
37941     renderPhase2 : function(){
37942         // render the rows now
37943         this.refresh();
37944         if(this.grid.autoSizeColumns){
37945             this.autoSizeColumns();
37946         }
37947     },
37948
37949     beforeInitialResize : function(){
37950
37951     },
37952
37953     onColumnSplitterMoved : function(i, w){
37954         this.userResized = true;
37955         var cm = this.grid.colModel;
37956         cm.setColumnWidth(i, w, true);
37957         var cid = cm.getColumnId(i);
37958         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37959         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37960         this.updateSplitters();
37961         this.layout();
37962         this.grid.fireEvent("columnresize", i, w);
37963     },
37964
37965     syncRowHeights : function(startIndex, endIndex){
37966         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37967             startIndex = startIndex || 0;
37968             var mrows = this.getBodyTable().rows;
37969             var lrows = this.getLockedTable().rows;
37970             var len = mrows.length-1;
37971             endIndex = Math.min(endIndex || len, len);
37972             for(var i = startIndex; i <= endIndex; i++){
37973                 var m = mrows[i], l = lrows[i];
37974                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37975                 m.style.height = l.style.height = h + "px";
37976             }
37977         }
37978     },
37979
37980     layout : function(initialRender, is2ndPass){
37981         var g = this.grid;
37982         var auto = g.autoHeight;
37983         var scrollOffset = 16;
37984         var c = g.getGridEl(), cm = this.cm,
37985                 expandCol = g.autoExpandColumn,
37986                 gv = this;
37987         //c.beginMeasure();
37988
37989         if(!c.dom.offsetWidth){ // display:none?
37990             if(initialRender){
37991                 this.lockedWrap.show();
37992                 this.mainWrap.show();
37993             }
37994             return;
37995         }
37996
37997         var hasLock = this.cm.isLocked(0);
37998
37999         var tbh = this.headerPanel.getHeight();
38000         var bbh = this.footerPanel.getHeight();
38001
38002         if(auto){
38003             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38004             var newHeight = ch + c.getBorderWidth("tb");
38005             if(g.maxHeight){
38006                 newHeight = Math.min(g.maxHeight, newHeight);
38007             }
38008             c.setHeight(newHeight);
38009         }
38010
38011         if(g.autoWidth){
38012             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38013         }
38014
38015         var s = this.scroller;
38016
38017         var csize = c.getSize(true);
38018
38019         this.el.setSize(csize.width, csize.height);
38020
38021         this.headerPanel.setWidth(csize.width);
38022         this.footerPanel.setWidth(csize.width);
38023
38024         var hdHeight = this.mainHd.getHeight();
38025         var vw = csize.width;
38026         var vh = csize.height - (tbh + bbh);
38027
38028         s.setSize(vw, vh);
38029
38030         var bt = this.getBodyTable();
38031         var ltWidth = hasLock ?
38032                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38033
38034         var scrollHeight = bt.offsetHeight;
38035         var scrollWidth = ltWidth + bt.offsetWidth;
38036         var vscroll = false, hscroll = false;
38037
38038         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38039
38040         var lw = this.lockedWrap, mw = this.mainWrap;
38041         var lb = this.lockedBody, mb = this.mainBody;
38042
38043         setTimeout(function(){
38044             var t = s.dom.offsetTop;
38045             var w = s.dom.clientWidth,
38046                 h = s.dom.clientHeight;
38047
38048             lw.setTop(t);
38049             lw.setSize(ltWidth, h);
38050
38051             mw.setLeftTop(ltWidth, t);
38052             mw.setSize(w-ltWidth, h);
38053
38054             lb.setHeight(h-hdHeight);
38055             mb.setHeight(h-hdHeight);
38056
38057             if(is2ndPass !== true && !gv.userResized && expandCol){
38058                 // high speed resize without full column calculation
38059                 
38060                 var ci = cm.getIndexById(expandCol);
38061                 if (ci < 0) {
38062                     ci = cm.findColumnIndex(expandCol);
38063                 }
38064                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38065                 var expandId = cm.getColumnId(ci);
38066                 var  tw = cm.getTotalWidth(false);
38067                 var currentWidth = cm.getColumnWidth(ci);
38068                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38069                 if(currentWidth != cw){
38070                     cm.setColumnWidth(ci, cw, true);
38071                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38072                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38073                     gv.updateSplitters();
38074                     gv.layout(false, true);
38075                 }
38076             }
38077
38078             if(initialRender){
38079                 lw.show();
38080                 mw.show();
38081             }
38082             //c.endMeasure();
38083         }, 10);
38084     },
38085
38086     onWindowResize : function(){
38087         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38088             return;
38089         }
38090         this.layout();
38091     },
38092
38093     appendFooter : function(parentEl){
38094         return null;
38095     },
38096
38097     sortAscText : "Sort Ascending",
38098     sortDescText : "Sort Descending",
38099     lockText : "Lock Column",
38100     unlockText : "Unlock Column",
38101     columnsText : "Columns"
38102 });
38103
38104
38105 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38106     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38107     this.proxy.el.addClass('x-grid3-col-dd');
38108 };
38109
38110 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38111     handleMouseDown : function(e){
38112
38113     },
38114
38115     callHandleMouseDown : function(e){
38116         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38117     }
38118 });
38119 /*
38120  * Based on:
38121  * Ext JS Library 1.1.1
38122  * Copyright(c) 2006-2007, Ext JS, LLC.
38123  *
38124  * Originally Released Under LGPL - original licence link has changed is not relivant.
38125  *
38126  * Fork - LGPL
38127  * <script type="text/javascript">
38128  */
38129  
38130 // private
38131 // This is a support class used internally by the Grid components
38132 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38133     this.grid = grid;
38134     this.view = grid.getView();
38135     this.proxy = this.view.resizeProxy;
38136     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38137         "gridSplitters" + this.grid.getGridEl().id, {
38138         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38139     });
38140     this.setHandleElId(Roo.id(hd));
38141     this.setOuterHandleElId(Roo.id(hd2));
38142     this.scroll = false;
38143 };
38144 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38145     fly: Roo.Element.fly,
38146
38147     b4StartDrag : function(x, y){
38148         this.view.headersDisabled = true;
38149         this.proxy.setHeight(this.view.mainWrap.getHeight());
38150         var w = this.cm.getColumnWidth(this.cellIndex);
38151         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38152         this.resetConstraints();
38153         this.setXConstraint(minw, 1000);
38154         this.setYConstraint(0, 0);
38155         this.minX = x - minw;
38156         this.maxX = x + 1000;
38157         this.startPos = x;
38158         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38159     },
38160
38161
38162     handleMouseDown : function(e){
38163         ev = Roo.EventObject.setEvent(e);
38164         var t = this.fly(ev.getTarget());
38165         if(t.hasClass("x-grid-split")){
38166             this.cellIndex = this.view.getCellIndex(t.dom);
38167             this.split = t.dom;
38168             this.cm = this.grid.colModel;
38169             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38170                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38171             }
38172         }
38173     },
38174
38175     endDrag : function(e){
38176         this.view.headersDisabled = false;
38177         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38178         var diff = endX - this.startPos;
38179         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38180     },
38181
38182     autoOffset : function(){
38183         this.setDelta(0,0);
38184     }
38185 });/*
38186  * Based on:
38187  * Ext JS Library 1.1.1
38188  * Copyright(c) 2006-2007, Ext JS, LLC.
38189  *
38190  * Originally Released Under LGPL - original licence link has changed is not relivant.
38191  *
38192  * Fork - LGPL
38193  * <script type="text/javascript">
38194  */
38195  
38196 // private
38197 // This is a support class used internally by the Grid components
38198 Roo.grid.GridDragZone = function(grid, config){
38199     this.view = grid.getView();
38200     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38201     if(this.view.lockedBody){
38202         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38203         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38204     }
38205     this.scroll = false;
38206     this.grid = grid;
38207     this.ddel = document.createElement('div');
38208     this.ddel.className = 'x-grid-dd-wrap';
38209 };
38210
38211 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38212     ddGroup : "GridDD",
38213
38214     getDragData : function(e){
38215         var t = Roo.lib.Event.getTarget(e);
38216         var rowIndex = this.view.findRowIndex(t);
38217         var sm = this.grid.selModel;
38218             
38219         //Roo.log(rowIndex);
38220         
38221         if (sm.getSelectedCell) {
38222             // cell selection..
38223             if (!sm.getSelectedCell()) {
38224                 return false;
38225             }
38226             if (rowIndex != sm.getSelectedCell()[0]) {
38227                 return false;
38228             }
38229         
38230         }
38231         
38232         if(rowIndex !== false){
38233             
38234             // if editorgrid.. 
38235             
38236             
38237             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38238                
38239             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38240               //  
38241             //}
38242             if (e.hasModifier()){
38243                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38244             }
38245             
38246             Roo.log("getDragData");
38247             
38248             return {
38249                 grid: this.grid,
38250                 ddel: this.ddel,
38251                 rowIndex: rowIndex,
38252                 selections:sm.getSelections ? sm.getSelections() : (
38253                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38254                 )
38255             };
38256         }
38257         return false;
38258     },
38259
38260     onInitDrag : function(e){
38261         var data = this.dragData;
38262         this.ddel.innerHTML = this.grid.getDragDropText();
38263         this.proxy.update(this.ddel);
38264         // fire start drag?
38265     },
38266
38267     afterRepair : function(){
38268         this.dragging = false;
38269     },
38270
38271     getRepairXY : function(e, data){
38272         return false;
38273     },
38274
38275     onEndDrag : function(data, e){
38276         // fire end drag?
38277     },
38278
38279     onValidDrop : function(dd, e, id){
38280         // fire drag drop?
38281         this.hideProxy();
38282     },
38283
38284     beforeInvalidDrop : function(e, id){
38285
38286     }
38287 });/*
38288  * Based on:
38289  * Ext JS Library 1.1.1
38290  * Copyright(c) 2006-2007, Ext JS, LLC.
38291  *
38292  * Originally Released Under LGPL - original licence link has changed is not relivant.
38293  *
38294  * Fork - LGPL
38295  * <script type="text/javascript">
38296  */
38297  
38298
38299 /**
38300  * @class Roo.grid.ColumnModel
38301  * @extends Roo.util.Observable
38302  * This is the default implementation of a ColumnModel used by the Grid. It defines
38303  * the columns in the grid.
38304  * <br>Usage:<br>
38305  <pre><code>
38306  var colModel = new Roo.grid.ColumnModel([
38307         {header: "Ticker", width: 60, sortable: true, locked: true},
38308         {header: "Company Name", width: 150, sortable: true},
38309         {header: "Market Cap.", width: 100, sortable: true},
38310         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38311         {header: "Employees", width: 100, sortable: true, resizable: false}
38312  ]);
38313  </code></pre>
38314  * <p>
38315  
38316  * The config options listed for this class are options which may appear in each
38317  * individual column definition.
38318  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38319  * @constructor
38320  * @param {Object} config An Array of column config objects. See this class's
38321  * config objects for details.
38322 */
38323 Roo.grid.ColumnModel = function(config){
38324         /**
38325      * The config passed into the constructor
38326      */
38327     this.config = config;
38328     this.lookup = {};
38329
38330     // if no id, create one
38331     // if the column does not have a dataIndex mapping,
38332     // map it to the order it is in the config
38333     for(var i = 0, len = config.length; i < len; i++){
38334         var c = config[i];
38335         if(typeof c.dataIndex == "undefined"){
38336             c.dataIndex = i;
38337         }
38338         if(typeof c.renderer == "string"){
38339             c.renderer = Roo.util.Format[c.renderer];
38340         }
38341         if(typeof c.id == "undefined"){
38342             c.id = Roo.id();
38343         }
38344         if(c.editor && c.editor.xtype){
38345             c.editor  = Roo.factory(c.editor, Roo.grid);
38346         }
38347         if(c.editor && c.editor.isFormField){
38348             c.editor = new Roo.grid.GridEditor(c.editor);
38349         }
38350         this.lookup[c.id] = c;
38351     }
38352
38353     /**
38354      * The width of columns which have no width specified (defaults to 100)
38355      * @type Number
38356      */
38357     this.defaultWidth = 100;
38358
38359     /**
38360      * Default sortable of columns which have no sortable specified (defaults to false)
38361      * @type Boolean
38362      */
38363     this.defaultSortable = false;
38364
38365     this.addEvents({
38366         /**
38367              * @event widthchange
38368              * Fires when the width of a column changes.
38369              * @param {ColumnModel} this
38370              * @param {Number} columnIndex The column index
38371              * @param {Number} newWidth The new width
38372              */
38373             "widthchange": true,
38374         /**
38375              * @event headerchange
38376              * Fires when the text of a header changes.
38377              * @param {ColumnModel} this
38378              * @param {Number} columnIndex The column index
38379              * @param {Number} newText The new header text
38380              */
38381             "headerchange": true,
38382         /**
38383              * @event hiddenchange
38384              * Fires when a column is hidden or "unhidden".
38385              * @param {ColumnModel} this
38386              * @param {Number} columnIndex The column index
38387              * @param {Boolean} hidden true if hidden, false otherwise
38388              */
38389             "hiddenchange": true,
38390             /**
38391          * @event columnmoved
38392          * Fires when a column is moved.
38393          * @param {ColumnModel} this
38394          * @param {Number} oldIndex
38395          * @param {Number} newIndex
38396          */
38397         "columnmoved" : true,
38398         /**
38399          * @event columlockchange
38400          * Fires when a column's locked state is changed
38401          * @param {ColumnModel} this
38402          * @param {Number} colIndex
38403          * @param {Boolean} locked true if locked
38404          */
38405         "columnlockchange" : true
38406     });
38407     Roo.grid.ColumnModel.superclass.constructor.call(this);
38408 };
38409 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38410     /**
38411      * @cfg {String} header The header text to display in the Grid view.
38412      */
38413     /**
38414      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38415      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38416      * specified, the column's index is used as an index into the Record's data Array.
38417      */
38418     /**
38419      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38420      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38421      */
38422     /**
38423      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38424      * Defaults to the value of the {@link #defaultSortable} property.
38425      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38426      */
38427     /**
38428      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38429      */
38430     /**
38431      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38432      */
38433     /**
38434      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38435      */
38436     /**
38437      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38438      */
38439     /**
38440      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38441      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38442      * default renderer uses the raw data value.
38443      */
38444        /**
38445      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38446      */
38447     /**
38448      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38449      */
38450
38451     /**
38452      * Returns the id of the column at the specified index.
38453      * @param {Number} index The column index
38454      * @return {String} the id
38455      */
38456     getColumnId : function(index){
38457         return this.config[index].id;
38458     },
38459
38460     /**
38461      * Returns the column for a specified id.
38462      * @param {String} id The column id
38463      * @return {Object} the column
38464      */
38465     getColumnById : function(id){
38466         return this.lookup[id];
38467     },
38468
38469     
38470     /**
38471      * Returns the column for a specified dataIndex.
38472      * @param {String} dataIndex The column dataIndex
38473      * @return {Object|Boolean} the column or false if not found
38474      */
38475     getColumnByDataIndex: function(dataIndex){
38476         var index = this.findColumnIndex(dataIndex);
38477         return index > -1 ? this.config[index] : false;
38478     },
38479     
38480     /**
38481      * Returns the index for a specified column id.
38482      * @param {String} id The column id
38483      * @return {Number} the index, or -1 if not found
38484      */
38485     getIndexById : function(id){
38486         for(var i = 0, len = this.config.length; i < len; i++){
38487             if(this.config[i].id == id){
38488                 return i;
38489             }
38490         }
38491         return -1;
38492     },
38493     
38494     /**
38495      * Returns the index for a specified column dataIndex.
38496      * @param {String} dataIndex The column dataIndex
38497      * @return {Number} the index, or -1 if not found
38498      */
38499     
38500     findColumnIndex : function(dataIndex){
38501         for(var i = 0, len = this.config.length; i < len; i++){
38502             if(this.config[i].dataIndex == dataIndex){
38503                 return i;
38504             }
38505         }
38506         return -1;
38507     },
38508     
38509     
38510     moveColumn : function(oldIndex, newIndex){
38511         var c = this.config[oldIndex];
38512         this.config.splice(oldIndex, 1);
38513         this.config.splice(newIndex, 0, c);
38514         this.dataMap = null;
38515         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38516     },
38517
38518     isLocked : function(colIndex){
38519         return this.config[colIndex].locked === true;
38520     },
38521
38522     setLocked : function(colIndex, value, suppressEvent){
38523         if(this.isLocked(colIndex) == value){
38524             return;
38525         }
38526         this.config[colIndex].locked = value;
38527         if(!suppressEvent){
38528             this.fireEvent("columnlockchange", this, colIndex, value);
38529         }
38530     },
38531
38532     getTotalLockedWidth : function(){
38533         var totalWidth = 0;
38534         for(var i = 0; i < this.config.length; i++){
38535             if(this.isLocked(i) && !this.isHidden(i)){
38536                 this.totalWidth += this.getColumnWidth(i);
38537             }
38538         }
38539         return totalWidth;
38540     },
38541
38542     getLockedCount : function(){
38543         for(var i = 0, len = this.config.length; i < len; i++){
38544             if(!this.isLocked(i)){
38545                 return i;
38546             }
38547         }
38548     },
38549
38550     /**
38551      * Returns the number of columns.
38552      * @return {Number}
38553      */
38554     getColumnCount : function(visibleOnly){
38555         if(visibleOnly === true){
38556             var c = 0;
38557             for(var i = 0, len = this.config.length; i < len; i++){
38558                 if(!this.isHidden(i)){
38559                     c++;
38560                 }
38561             }
38562             return c;
38563         }
38564         return this.config.length;
38565     },
38566
38567     /**
38568      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38569      * @param {Function} fn
38570      * @param {Object} scope (optional)
38571      * @return {Array} result
38572      */
38573     getColumnsBy : function(fn, scope){
38574         var r = [];
38575         for(var i = 0, len = this.config.length; i < len; i++){
38576             var c = this.config[i];
38577             if(fn.call(scope||this, c, i) === true){
38578                 r[r.length] = c;
38579             }
38580         }
38581         return r;
38582     },
38583
38584     /**
38585      * Returns true if the specified column is sortable.
38586      * @param {Number} col The column index
38587      * @return {Boolean}
38588      */
38589     isSortable : function(col){
38590         if(typeof this.config[col].sortable == "undefined"){
38591             return this.defaultSortable;
38592         }
38593         return this.config[col].sortable;
38594     },
38595
38596     /**
38597      * Returns the rendering (formatting) function defined for the column.
38598      * @param {Number} col The column index.
38599      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38600      */
38601     getRenderer : function(col){
38602         if(!this.config[col].renderer){
38603             return Roo.grid.ColumnModel.defaultRenderer;
38604         }
38605         return this.config[col].renderer;
38606     },
38607
38608     /**
38609      * Sets the rendering (formatting) function for a column.
38610      * @param {Number} col The column index
38611      * @param {Function} fn The function to use to process the cell's raw data
38612      * to return HTML markup for the grid view. The render function is called with
38613      * the following parameters:<ul>
38614      * <li>Data value.</li>
38615      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38616      * <li>css A CSS style string to apply to the table cell.</li>
38617      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38618      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38619      * <li>Row index</li>
38620      * <li>Column index</li>
38621      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38622      */
38623     setRenderer : function(col, fn){
38624         this.config[col].renderer = fn;
38625     },
38626
38627     /**
38628      * Returns the width for the specified column.
38629      * @param {Number} col The column index
38630      * @return {Number}
38631      */
38632     getColumnWidth : function(col){
38633         return this.config[col].width * 1 || this.defaultWidth;
38634     },
38635
38636     /**
38637      * Sets the width for a column.
38638      * @param {Number} col The column index
38639      * @param {Number} width The new width
38640      */
38641     setColumnWidth : function(col, width, suppressEvent){
38642         this.config[col].width = width;
38643         this.totalWidth = null;
38644         if(!suppressEvent){
38645              this.fireEvent("widthchange", this, col, width);
38646         }
38647     },
38648
38649     /**
38650      * Returns the total width of all columns.
38651      * @param {Boolean} includeHidden True to include hidden column widths
38652      * @return {Number}
38653      */
38654     getTotalWidth : function(includeHidden){
38655         if(!this.totalWidth){
38656             this.totalWidth = 0;
38657             for(var i = 0, len = this.config.length; i < len; i++){
38658                 if(includeHidden || !this.isHidden(i)){
38659                     this.totalWidth += this.getColumnWidth(i);
38660                 }
38661             }
38662         }
38663         return this.totalWidth;
38664     },
38665
38666     /**
38667      * Returns the header for the specified column.
38668      * @param {Number} col The column index
38669      * @return {String}
38670      */
38671     getColumnHeader : function(col){
38672         return this.config[col].header;
38673     },
38674
38675     /**
38676      * Sets the header for a column.
38677      * @param {Number} col The column index
38678      * @param {String} header The new header
38679      */
38680     setColumnHeader : function(col, header){
38681         this.config[col].header = header;
38682         this.fireEvent("headerchange", this, col, header);
38683     },
38684
38685     /**
38686      * Returns the tooltip for the specified column.
38687      * @param {Number} col The column index
38688      * @return {String}
38689      */
38690     getColumnTooltip : function(col){
38691             return this.config[col].tooltip;
38692     },
38693     /**
38694      * Sets the tooltip for a column.
38695      * @param {Number} col The column index
38696      * @param {String} tooltip The new tooltip
38697      */
38698     setColumnTooltip : function(col, tooltip){
38699             this.config[col].tooltip = tooltip;
38700     },
38701
38702     /**
38703      * Returns the dataIndex for the specified column.
38704      * @param {Number} col The column index
38705      * @return {Number}
38706      */
38707     getDataIndex : function(col){
38708         return this.config[col].dataIndex;
38709     },
38710
38711     /**
38712      * Sets the dataIndex for a column.
38713      * @param {Number} col The column index
38714      * @param {Number} dataIndex The new dataIndex
38715      */
38716     setDataIndex : function(col, dataIndex){
38717         this.config[col].dataIndex = dataIndex;
38718     },
38719
38720     
38721     
38722     /**
38723      * Returns true if the cell is editable.
38724      * @param {Number} colIndex The column index
38725      * @param {Number} rowIndex The row index
38726      * @return {Boolean}
38727      */
38728     isCellEditable : function(colIndex, rowIndex){
38729         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38730     },
38731
38732     /**
38733      * Returns the editor defined for the cell/column.
38734      * return false or null to disable editing.
38735      * @param {Number} colIndex The column index
38736      * @param {Number} rowIndex The row index
38737      * @return {Object}
38738      */
38739     getCellEditor : function(colIndex, rowIndex){
38740         return this.config[colIndex].editor;
38741     },
38742
38743     /**
38744      * Sets if a column is editable.
38745      * @param {Number} col The column index
38746      * @param {Boolean} editable True if the column is editable
38747      */
38748     setEditable : function(col, editable){
38749         this.config[col].editable = editable;
38750     },
38751
38752
38753     /**
38754      * Returns true if the column is hidden.
38755      * @param {Number} colIndex The column index
38756      * @return {Boolean}
38757      */
38758     isHidden : function(colIndex){
38759         return this.config[colIndex].hidden;
38760     },
38761
38762
38763     /**
38764      * Returns true if the column width cannot be changed
38765      */
38766     isFixed : function(colIndex){
38767         return this.config[colIndex].fixed;
38768     },
38769
38770     /**
38771      * Returns true if the column can be resized
38772      * @return {Boolean}
38773      */
38774     isResizable : function(colIndex){
38775         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38776     },
38777     /**
38778      * Sets if a column is hidden.
38779      * @param {Number} colIndex The column index
38780      * @param {Boolean} hidden True if the column is hidden
38781      */
38782     setHidden : function(colIndex, hidden){
38783         this.config[colIndex].hidden = hidden;
38784         this.totalWidth = null;
38785         this.fireEvent("hiddenchange", this, colIndex, hidden);
38786     },
38787
38788     /**
38789      * Sets the editor for a column.
38790      * @param {Number} col The column index
38791      * @param {Object} editor The editor object
38792      */
38793     setEditor : function(col, editor){
38794         this.config[col].editor = editor;
38795     }
38796 });
38797
38798 Roo.grid.ColumnModel.defaultRenderer = function(value){
38799         if(typeof value == "string" && value.length < 1){
38800             return "&#160;";
38801         }
38802         return value;
38803 };
38804
38805 // Alias for backwards compatibility
38806 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38807 /*
38808  * Based on:
38809  * Ext JS Library 1.1.1
38810  * Copyright(c) 2006-2007, Ext JS, LLC.
38811  *
38812  * Originally Released Under LGPL - original licence link has changed is not relivant.
38813  *
38814  * Fork - LGPL
38815  * <script type="text/javascript">
38816  */
38817
38818 /**
38819  * @class Roo.grid.AbstractSelectionModel
38820  * @extends Roo.util.Observable
38821  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38822  * implemented by descendant classes.  This class should not be directly instantiated.
38823  * @constructor
38824  */
38825 Roo.grid.AbstractSelectionModel = function(){
38826     this.locked = false;
38827     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38828 };
38829
38830 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38831     /** @ignore Called by the grid automatically. Do not call directly. */
38832     init : function(grid){
38833         this.grid = grid;
38834         this.initEvents();
38835     },
38836
38837     /**
38838      * Locks the selections.
38839      */
38840     lock : function(){
38841         this.locked = true;
38842     },
38843
38844     /**
38845      * Unlocks the selections.
38846      */
38847     unlock : function(){
38848         this.locked = false;
38849     },
38850
38851     /**
38852      * Returns true if the selections are locked.
38853      * @return {Boolean}
38854      */
38855     isLocked : function(){
38856         return this.locked;
38857     }
38858 });/*
38859  * Based on:
38860  * Ext JS Library 1.1.1
38861  * Copyright(c) 2006-2007, Ext JS, LLC.
38862  *
38863  * Originally Released Under LGPL - original licence link has changed is not relivant.
38864  *
38865  * Fork - LGPL
38866  * <script type="text/javascript">
38867  */
38868 /**
38869  * @extends Roo.grid.AbstractSelectionModel
38870  * @class Roo.grid.RowSelectionModel
38871  * The default SelectionModel used by {@link Roo.grid.Grid}.
38872  * It supports multiple selections and keyboard selection/navigation. 
38873  * @constructor
38874  * @param {Object} config
38875  */
38876 Roo.grid.RowSelectionModel = function(config){
38877     Roo.apply(this, config);
38878     this.selections = new Roo.util.MixedCollection(false, function(o){
38879         return o.id;
38880     });
38881
38882     this.last = false;
38883     this.lastActive = false;
38884
38885     this.addEvents({
38886         /**
38887              * @event selectionchange
38888              * Fires when the selection changes
38889              * @param {SelectionModel} this
38890              */
38891             "selectionchange" : true,
38892         /**
38893              * @event afterselectionchange
38894              * Fires after the selection changes (eg. by key press or clicking)
38895              * @param {SelectionModel} this
38896              */
38897             "afterselectionchange" : true,
38898         /**
38899              * @event beforerowselect
38900              * Fires when a row is selected being selected, return false to cancel.
38901              * @param {SelectionModel} this
38902              * @param {Number} rowIndex The selected index
38903              * @param {Boolean} keepExisting False if other selections will be cleared
38904              */
38905             "beforerowselect" : true,
38906         /**
38907              * @event rowselect
38908              * Fires when a row is selected.
38909              * @param {SelectionModel} this
38910              * @param {Number} rowIndex The selected index
38911              * @param {Roo.data.Record} r The record
38912              */
38913             "rowselect" : true,
38914         /**
38915              * @event rowdeselect
38916              * Fires when a row is deselected.
38917              * @param {SelectionModel} this
38918              * @param {Number} rowIndex The selected index
38919              */
38920         "rowdeselect" : true
38921     });
38922     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38923     this.locked = false;
38924 };
38925
38926 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38927     /**
38928      * @cfg {Boolean} singleSelect
38929      * True to allow selection of only one row at a time (defaults to false)
38930      */
38931     singleSelect : false,
38932
38933     // private
38934     initEvents : function(){
38935
38936         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38937             this.grid.on("mousedown", this.handleMouseDown, this);
38938         }else{ // allow click to work like normal
38939             this.grid.on("rowclick", this.handleDragableRowClick, this);
38940         }
38941
38942         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38943             "up" : function(e){
38944                 if(!e.shiftKey){
38945                     this.selectPrevious(e.shiftKey);
38946                 }else if(this.last !== false && this.lastActive !== false){
38947                     var last = this.last;
38948                     this.selectRange(this.last,  this.lastActive-1);
38949                     this.grid.getView().focusRow(this.lastActive);
38950                     if(last !== false){
38951                         this.last = last;
38952                     }
38953                 }else{
38954                     this.selectFirstRow();
38955                 }
38956                 this.fireEvent("afterselectionchange", this);
38957             },
38958             "down" : function(e){
38959                 if(!e.shiftKey){
38960                     this.selectNext(e.shiftKey);
38961                 }else if(this.last !== false && this.lastActive !== false){
38962                     var last = this.last;
38963                     this.selectRange(this.last,  this.lastActive+1);
38964                     this.grid.getView().focusRow(this.lastActive);
38965                     if(last !== false){
38966                         this.last = last;
38967                     }
38968                 }else{
38969                     this.selectFirstRow();
38970                 }
38971                 this.fireEvent("afterselectionchange", this);
38972             },
38973             scope: this
38974         });
38975
38976         var view = this.grid.view;
38977         view.on("refresh", this.onRefresh, this);
38978         view.on("rowupdated", this.onRowUpdated, this);
38979         view.on("rowremoved", this.onRemove, this);
38980     },
38981
38982     // private
38983     onRefresh : function(){
38984         var ds = this.grid.dataSource, i, v = this.grid.view;
38985         var s = this.selections;
38986         s.each(function(r){
38987             if((i = ds.indexOfId(r.id)) != -1){
38988                 v.onRowSelect(i);
38989             }else{
38990                 s.remove(r);
38991             }
38992         });
38993     },
38994
38995     // private
38996     onRemove : function(v, index, r){
38997         this.selections.remove(r);
38998     },
38999
39000     // private
39001     onRowUpdated : function(v, index, r){
39002         if(this.isSelected(r)){
39003             v.onRowSelect(index);
39004         }
39005     },
39006
39007     /**
39008      * Select records.
39009      * @param {Array} records The records to select
39010      * @param {Boolean} keepExisting (optional) True to keep existing selections
39011      */
39012     selectRecords : function(records, keepExisting){
39013         if(!keepExisting){
39014             this.clearSelections();
39015         }
39016         var ds = this.grid.dataSource;
39017         for(var i = 0, len = records.length; i < len; i++){
39018             this.selectRow(ds.indexOf(records[i]), true);
39019         }
39020     },
39021
39022     /**
39023      * Gets the number of selected rows.
39024      * @return {Number}
39025      */
39026     getCount : function(){
39027         return this.selections.length;
39028     },
39029
39030     /**
39031      * Selects the first row in the grid.
39032      */
39033     selectFirstRow : function(){
39034         this.selectRow(0);
39035     },
39036
39037     /**
39038      * Select the last row.
39039      * @param {Boolean} keepExisting (optional) True to keep existing selections
39040      */
39041     selectLastRow : function(keepExisting){
39042         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39043     },
39044
39045     /**
39046      * Selects the row immediately following the last selected row.
39047      * @param {Boolean} keepExisting (optional) True to keep existing selections
39048      */
39049     selectNext : function(keepExisting){
39050         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39051             this.selectRow(this.last+1, keepExisting);
39052             this.grid.getView().focusRow(this.last);
39053         }
39054     },
39055
39056     /**
39057      * Selects the row that precedes the last selected row.
39058      * @param {Boolean} keepExisting (optional) True to keep existing selections
39059      */
39060     selectPrevious : function(keepExisting){
39061         if(this.last){
39062             this.selectRow(this.last-1, keepExisting);
39063             this.grid.getView().focusRow(this.last);
39064         }
39065     },
39066
39067     /**
39068      * Returns the selected records
39069      * @return {Array} Array of selected records
39070      */
39071     getSelections : function(){
39072         return [].concat(this.selections.items);
39073     },
39074
39075     /**
39076      * Returns the first selected record.
39077      * @return {Record}
39078      */
39079     getSelected : function(){
39080         return this.selections.itemAt(0);
39081     },
39082
39083
39084     /**
39085      * Clears all selections.
39086      */
39087     clearSelections : function(fast){
39088         if(this.locked) return;
39089         if(fast !== true){
39090             var ds = this.grid.dataSource;
39091             var s = this.selections;
39092             s.each(function(r){
39093                 this.deselectRow(ds.indexOfId(r.id));
39094             }, this);
39095             s.clear();
39096         }else{
39097             this.selections.clear();
39098         }
39099         this.last = false;
39100     },
39101
39102
39103     /**
39104      * Selects all rows.
39105      */
39106     selectAll : function(){
39107         if(this.locked) return;
39108         this.selections.clear();
39109         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39110             this.selectRow(i, true);
39111         }
39112     },
39113
39114     /**
39115      * Returns True if there is a selection.
39116      * @return {Boolean}
39117      */
39118     hasSelection : function(){
39119         return this.selections.length > 0;
39120     },
39121
39122     /**
39123      * Returns True if the specified row is selected.
39124      * @param {Number/Record} record The record or index of the record to check
39125      * @return {Boolean}
39126      */
39127     isSelected : function(index){
39128         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39129         return (r && this.selections.key(r.id) ? true : false);
39130     },
39131
39132     /**
39133      * Returns True if the specified record id is selected.
39134      * @param {String} id The id of record to check
39135      * @return {Boolean}
39136      */
39137     isIdSelected : function(id){
39138         return (this.selections.key(id) ? true : false);
39139     },
39140
39141     // private
39142     handleMouseDown : function(e, t){
39143         var view = this.grid.getView(), rowIndex;
39144         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39145             return;
39146         };
39147         if(e.shiftKey && this.last !== false){
39148             var last = this.last;
39149             this.selectRange(last, rowIndex, e.ctrlKey);
39150             this.last = last; // reset the last
39151             view.focusRow(rowIndex);
39152         }else{
39153             var isSelected = this.isSelected(rowIndex);
39154             if(e.button !== 0 && isSelected){
39155                 view.focusRow(rowIndex);
39156             }else if(e.ctrlKey && isSelected){
39157                 this.deselectRow(rowIndex);
39158             }else if(!isSelected){
39159                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39160                 view.focusRow(rowIndex);
39161             }
39162         }
39163         this.fireEvent("afterselectionchange", this);
39164     },
39165     // private
39166     handleDragableRowClick :  function(grid, rowIndex, e) 
39167     {
39168         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39169             this.selectRow(rowIndex, false);
39170             grid.view.focusRow(rowIndex);
39171              this.fireEvent("afterselectionchange", this);
39172         }
39173     },
39174     
39175     /**
39176      * Selects multiple rows.
39177      * @param {Array} rows Array of the indexes of the row to select
39178      * @param {Boolean} keepExisting (optional) True to keep existing selections
39179      */
39180     selectRows : function(rows, keepExisting){
39181         if(!keepExisting){
39182             this.clearSelections();
39183         }
39184         for(var i = 0, len = rows.length; i < len; i++){
39185             this.selectRow(rows[i], true);
39186         }
39187     },
39188
39189     /**
39190      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39191      * @param {Number} startRow The index of the first row in the range
39192      * @param {Number} endRow The index of the last row in the range
39193      * @param {Boolean} keepExisting (optional) True to retain existing selections
39194      */
39195     selectRange : function(startRow, endRow, keepExisting){
39196         if(this.locked) return;
39197         if(!keepExisting){
39198             this.clearSelections();
39199         }
39200         if(startRow <= endRow){
39201             for(var i = startRow; i <= endRow; i++){
39202                 this.selectRow(i, true);
39203             }
39204         }else{
39205             for(var i = startRow; i >= endRow; i--){
39206                 this.selectRow(i, true);
39207             }
39208         }
39209     },
39210
39211     /**
39212      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39213      * @param {Number} startRow The index of the first row in the range
39214      * @param {Number} endRow The index of the last row in the range
39215      */
39216     deselectRange : function(startRow, endRow, preventViewNotify){
39217         if(this.locked) return;
39218         for(var i = startRow; i <= endRow; i++){
39219             this.deselectRow(i, preventViewNotify);
39220         }
39221     },
39222
39223     /**
39224      * Selects a row.
39225      * @param {Number} row The index of the row to select
39226      * @param {Boolean} keepExisting (optional) True to keep existing selections
39227      */
39228     selectRow : function(index, keepExisting, preventViewNotify){
39229         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39230         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39231             if(!keepExisting || this.singleSelect){
39232                 this.clearSelections();
39233             }
39234             var r = this.grid.dataSource.getAt(index);
39235             this.selections.add(r);
39236             this.last = this.lastActive = index;
39237             if(!preventViewNotify){
39238                 this.grid.getView().onRowSelect(index);
39239             }
39240             this.fireEvent("rowselect", this, index, r);
39241             this.fireEvent("selectionchange", this);
39242         }
39243     },
39244
39245     /**
39246      * Deselects a row.
39247      * @param {Number} row The index of the row to deselect
39248      */
39249     deselectRow : function(index, preventViewNotify){
39250         if(this.locked) return;
39251         if(this.last == index){
39252             this.last = false;
39253         }
39254         if(this.lastActive == index){
39255             this.lastActive = false;
39256         }
39257         var r = this.grid.dataSource.getAt(index);
39258         this.selections.remove(r);
39259         if(!preventViewNotify){
39260             this.grid.getView().onRowDeselect(index);
39261         }
39262         this.fireEvent("rowdeselect", this, index);
39263         this.fireEvent("selectionchange", this);
39264     },
39265
39266     // private
39267     restoreLast : function(){
39268         if(this._last){
39269             this.last = this._last;
39270         }
39271     },
39272
39273     // private
39274     acceptsNav : function(row, col, cm){
39275         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39276     },
39277
39278     // private
39279     onEditorKey : function(field, e){
39280         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39281         if(k == e.TAB){
39282             e.stopEvent();
39283             ed.completeEdit();
39284             if(e.shiftKey){
39285                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39286             }else{
39287                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39288             }
39289         }else if(k == e.ENTER && !e.ctrlKey){
39290             e.stopEvent();
39291             ed.completeEdit();
39292             if(e.shiftKey){
39293                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39294             }else{
39295                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39296             }
39297         }else if(k == e.ESC){
39298             ed.cancelEdit();
39299         }
39300         if(newCell){
39301             g.startEditing(newCell[0], newCell[1]);
39302         }
39303     }
39304 });/*
39305  * Based on:
39306  * Ext JS Library 1.1.1
39307  * Copyright(c) 2006-2007, Ext JS, LLC.
39308  *
39309  * Originally Released Under LGPL - original licence link has changed is not relivant.
39310  *
39311  * Fork - LGPL
39312  * <script type="text/javascript">
39313  */
39314 /**
39315  * @class Roo.grid.CellSelectionModel
39316  * @extends Roo.grid.AbstractSelectionModel
39317  * This class provides the basic implementation for cell selection in a grid.
39318  * @constructor
39319  * @param {Object} config The object containing the configuration of this model.
39320  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39321  */
39322 Roo.grid.CellSelectionModel = function(config){
39323     Roo.apply(this, config);
39324
39325     this.selection = null;
39326
39327     this.addEvents({
39328         /**
39329              * @event beforerowselect
39330              * Fires before a cell is selected.
39331              * @param {SelectionModel} this
39332              * @param {Number} rowIndex The selected row index
39333              * @param {Number} colIndex The selected cell index
39334              */
39335             "beforecellselect" : true,
39336         /**
39337              * @event cellselect
39338              * Fires when a cell is selected.
39339              * @param {SelectionModel} this
39340              * @param {Number} rowIndex The selected row index
39341              * @param {Number} colIndex The selected cell index
39342              */
39343             "cellselect" : true,
39344         /**
39345              * @event selectionchange
39346              * Fires when the active selection changes.
39347              * @param {SelectionModel} this
39348              * @param {Object} selection null for no selection or an object (o) with two properties
39349                 <ul>
39350                 <li>o.record: the record object for the row the selection is in</li>
39351                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39352                 </ul>
39353              */
39354             "selectionchange" : true,
39355         /**
39356              * @event tabend
39357              * Fires when the tab (or enter) was pressed on the last editable cell
39358              * You can use this to trigger add new row.
39359              * @param {SelectionModel} this
39360              */
39361             "tabend" : true,
39362          /**
39363              * @event beforeeditnext
39364              * Fires before the next editable sell is made active
39365              * You can use this to skip to another cell or fire the tabend
39366              *    if you set cell to false
39367              * @param {Object} eventdata object : { cell : [ row, col ] } 
39368              */
39369             "beforeeditnext" : true
39370     });
39371     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39372 };
39373
39374 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39375     
39376     enter_is_tab: false,
39377
39378     /** @ignore */
39379     initEvents : function(){
39380         this.grid.on("mousedown", this.handleMouseDown, this);
39381         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39382         var view = this.grid.view;
39383         view.on("refresh", this.onViewChange, this);
39384         view.on("rowupdated", this.onRowUpdated, this);
39385         view.on("beforerowremoved", this.clearSelections, this);
39386         view.on("beforerowsinserted", this.clearSelections, this);
39387         if(this.grid.isEditor){
39388             this.grid.on("beforeedit", this.beforeEdit,  this);
39389         }
39390     },
39391
39392         //private
39393     beforeEdit : function(e){
39394         this.select(e.row, e.column, false, true, e.record);
39395     },
39396
39397         //private
39398     onRowUpdated : function(v, index, r){
39399         if(this.selection && this.selection.record == r){
39400             v.onCellSelect(index, this.selection.cell[1]);
39401         }
39402     },
39403
39404         //private
39405     onViewChange : function(){
39406         this.clearSelections(true);
39407     },
39408
39409         /**
39410          * Returns the currently selected cell,.
39411          * @return {Array} The selected cell (row, column) or null if none selected.
39412          */
39413     getSelectedCell : function(){
39414         return this.selection ? this.selection.cell : null;
39415     },
39416
39417     /**
39418      * Clears all selections.
39419      * @param {Boolean} true to prevent the gridview from being notified about the change.
39420      */
39421     clearSelections : function(preventNotify){
39422         var s = this.selection;
39423         if(s){
39424             if(preventNotify !== true){
39425                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39426             }
39427             this.selection = null;
39428             this.fireEvent("selectionchange", this, null);
39429         }
39430     },
39431
39432     /**
39433      * Returns true if there is a selection.
39434      * @return {Boolean}
39435      */
39436     hasSelection : function(){
39437         return this.selection ? true : false;
39438     },
39439
39440     /** @ignore */
39441     handleMouseDown : function(e, t){
39442         var v = this.grid.getView();
39443         if(this.isLocked()){
39444             return;
39445         };
39446         var row = v.findRowIndex(t);
39447         var cell = v.findCellIndex(t);
39448         if(row !== false && cell !== false){
39449             this.select(row, cell);
39450         }
39451     },
39452
39453     /**
39454      * Selects a cell.
39455      * @param {Number} rowIndex
39456      * @param {Number} collIndex
39457      */
39458     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39459         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39460             this.clearSelections();
39461             r = r || this.grid.dataSource.getAt(rowIndex);
39462             this.selection = {
39463                 record : r,
39464                 cell : [rowIndex, colIndex]
39465             };
39466             if(!preventViewNotify){
39467                 var v = this.grid.getView();
39468                 v.onCellSelect(rowIndex, colIndex);
39469                 if(preventFocus !== true){
39470                     v.focusCell(rowIndex, colIndex);
39471                 }
39472             }
39473             this.fireEvent("cellselect", this, rowIndex, colIndex);
39474             this.fireEvent("selectionchange", this, this.selection);
39475         }
39476     },
39477
39478         //private
39479     isSelectable : function(rowIndex, colIndex, cm){
39480         return !cm.isHidden(colIndex);
39481     },
39482
39483     /** @ignore */
39484     handleKeyDown : function(e){
39485         //Roo.log('Cell Sel Model handleKeyDown');
39486         if(!e.isNavKeyPress()){
39487             return;
39488         }
39489         var g = this.grid, s = this.selection;
39490         if(!s){
39491             e.stopEvent();
39492             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39493             if(cell){
39494                 this.select(cell[0], cell[1]);
39495             }
39496             return;
39497         }
39498         var sm = this;
39499         var walk = function(row, col, step){
39500             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39501         };
39502         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39503         var newCell;
39504
39505       
39506
39507         switch(k){
39508             case e.TAB:
39509                 // handled by onEditorKey
39510                 if (g.isEditor && g.editing) {
39511                     return;
39512                 }
39513                 if(e.shiftKey) {
39514                     newCell = walk(r, c-1, -1);
39515                 } else {
39516                     newCell = walk(r, c+1, 1);
39517                 }
39518                 break;
39519             
39520             case e.DOWN:
39521                newCell = walk(r+1, c, 1);
39522                 break;
39523             
39524             case e.UP:
39525                 newCell = walk(r-1, c, -1);
39526                 break;
39527             
39528             case e.RIGHT:
39529                 newCell = walk(r, c+1, 1);
39530                 break;
39531             
39532             case e.LEFT:
39533                 newCell = walk(r, c-1, -1);
39534                 break;
39535             
39536             case e.ENTER:
39537                 
39538                 if(g.isEditor && !g.editing){
39539                    g.startEditing(r, c);
39540                    e.stopEvent();
39541                    return;
39542                 }
39543                 
39544                 
39545              break;
39546         };
39547         if(newCell){
39548             this.select(newCell[0], newCell[1]);
39549             e.stopEvent();
39550             
39551         }
39552     },
39553
39554     acceptsNav : function(row, col, cm){
39555         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39556     },
39557     /**
39558      * Selects a cell.
39559      * @param {Number} field (not used) - as it's normally used as a listener
39560      * @param {Number} e - event - fake it by using
39561      *
39562      * var e = Roo.EventObjectImpl.prototype;
39563      * e.keyCode = e.TAB
39564      *
39565      * 
39566      */
39567     onEditorKey : function(field, e){
39568         
39569         var k = e.getKey(),
39570             newCell,
39571             g = this.grid,
39572             ed = g.activeEditor,
39573             forward = false;
39574         ///Roo.log('onEditorKey' + k);
39575         
39576         
39577         if (this.enter_is_tab && k == e.ENTER) {
39578             k = e.TAB;
39579         }
39580         
39581         if(k == e.TAB){
39582             if(e.shiftKey){
39583                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39584             }else{
39585                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39586                 forward = true;
39587             }
39588             
39589             e.stopEvent();
39590             
39591         } else if(k == e.ENTER &&  !e.ctrlKey){
39592             ed.completeEdit();
39593             e.stopEvent();
39594             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39595         
39596                 } else if(k == e.ESC){
39597             ed.cancelEdit();
39598         }
39599                 
39600         if (newCell) {
39601             var ecall = { cell : newCell, forward : forward };
39602             this.fireEvent('beforeeditnext', ecall );
39603             newCell = ecall.cell;
39604                         forward = ecall.forward;
39605         }
39606                 
39607         if(newCell){
39608             //Roo.log('next cell after edit');
39609             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39610         } else if (forward) {
39611             // tabbed past last
39612             this.fireEvent.defer(100, this, ['tabend',this]);
39613         }
39614     }
39615 });/*
39616  * Based on:
39617  * Ext JS Library 1.1.1
39618  * Copyright(c) 2006-2007, Ext JS, LLC.
39619  *
39620  * Originally Released Under LGPL - original licence link has changed is not relivant.
39621  *
39622  * Fork - LGPL
39623  * <script type="text/javascript">
39624  */
39625  
39626 /**
39627  * @class Roo.grid.EditorGrid
39628  * @extends Roo.grid.Grid
39629  * Class for creating and editable grid.
39630  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39631  * The container MUST have some type of size defined for the grid to fill. The container will be 
39632  * automatically set to position relative if it isn't already.
39633  * @param {Object} dataSource The data model to bind to
39634  * @param {Object} colModel The column model with info about this grid's columns
39635  */
39636 Roo.grid.EditorGrid = function(container, config){
39637     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39638     this.getGridEl().addClass("xedit-grid");
39639
39640     if(!this.selModel){
39641         this.selModel = new Roo.grid.CellSelectionModel();
39642     }
39643
39644     this.activeEditor = null;
39645
39646         this.addEvents({
39647             /**
39648              * @event beforeedit
39649              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39650              * <ul style="padding:5px;padding-left:16px;">
39651              * <li>grid - This grid</li>
39652              * <li>record - The record being edited</li>
39653              * <li>field - The field name being edited</li>
39654              * <li>value - The value for the field being edited.</li>
39655              * <li>row - The grid row index</li>
39656              * <li>column - The grid column index</li>
39657              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39658              * </ul>
39659              * @param {Object} e An edit event (see above for description)
39660              */
39661             "beforeedit" : true,
39662             /**
39663              * @event afteredit
39664              * Fires after a cell is edited. <br />
39665              * <ul style="padding:5px;padding-left:16px;">
39666              * <li>grid - This grid</li>
39667              * <li>record - The record being edited</li>
39668              * <li>field - The field name being edited</li>
39669              * <li>value - The value being set</li>
39670              * <li>originalValue - The original value for the field, before the edit.</li>
39671              * <li>row - The grid row index</li>
39672              * <li>column - The grid column index</li>
39673              * </ul>
39674              * @param {Object} e An edit event (see above for description)
39675              */
39676             "afteredit" : true,
39677             /**
39678              * @event validateedit
39679              * Fires after a cell is edited, but before the value is set in the record. 
39680          * You can use this to modify the value being set in the field, Return false
39681              * to cancel the change. The edit event object has the following properties <br />
39682              * <ul style="padding:5px;padding-left:16px;">
39683          * <li>editor - This editor</li>
39684              * <li>grid - This grid</li>
39685              * <li>record - The record being edited</li>
39686              * <li>field - The field name being edited</li>
39687              * <li>value - The value being set</li>
39688              * <li>originalValue - The original value for the field, before the edit.</li>
39689              * <li>row - The grid row index</li>
39690              * <li>column - The grid column index</li>
39691              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39692              * </ul>
39693              * @param {Object} e An edit event (see above for description)
39694              */
39695             "validateedit" : true
39696         });
39697     this.on("bodyscroll", this.stopEditing,  this);
39698     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39699 };
39700
39701 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39702     /**
39703      * @cfg {Number} clicksToEdit
39704      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39705      */
39706     clicksToEdit: 2,
39707
39708     // private
39709     isEditor : true,
39710     // private
39711     trackMouseOver: false, // causes very odd FF errors
39712
39713     onCellDblClick : function(g, row, col){
39714         this.startEditing(row, col);
39715     },
39716
39717     onEditComplete : function(ed, value, startValue){
39718         this.editing = false;
39719         this.activeEditor = null;
39720         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39721         var r = ed.record;
39722         var field = this.colModel.getDataIndex(ed.col);
39723         var e = {
39724             grid: this,
39725             record: r,
39726             field: field,
39727             originalValue: startValue,
39728             value: value,
39729             row: ed.row,
39730             column: ed.col,
39731             cancel:false,
39732             editor: ed
39733         };
39734         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39735         cell.show();
39736           
39737         if(String(value) !== String(startValue)){
39738             
39739             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39740                 r.set(field, e.value);
39741                 // if we are dealing with a combo box..
39742                 // then we also set the 'name' colum to be the displayField
39743                 if (ed.field.displayField && ed.field.name) {
39744                     r.set(ed.field.name, ed.field.el.dom.value);
39745                 }
39746                 
39747                 delete e.cancel; //?? why!!!
39748                 this.fireEvent("afteredit", e);
39749             }
39750         } else {
39751             this.fireEvent("afteredit", e); // always fire it!
39752         }
39753         this.view.focusCell(ed.row, ed.col);
39754     },
39755
39756     /**
39757      * Starts editing the specified for the specified row/column
39758      * @param {Number} rowIndex
39759      * @param {Number} colIndex
39760      */
39761     startEditing : function(row, col){
39762         this.stopEditing();
39763         if(this.colModel.isCellEditable(col, row)){
39764             this.view.ensureVisible(row, col, true);
39765           
39766             var r = this.dataSource.getAt(row);
39767             var field = this.colModel.getDataIndex(col);
39768             var cell = Roo.get(this.view.getCell(row,col));
39769             var e = {
39770                 grid: this,
39771                 record: r,
39772                 field: field,
39773                 value: r.data[field],
39774                 row: row,
39775                 column: col,
39776                 cancel:false 
39777             };
39778             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39779                 this.editing = true;
39780                 var ed = this.colModel.getCellEditor(col, row);
39781                 
39782                 if (!ed) {
39783                     return;
39784                 }
39785                 if(!ed.rendered){
39786                     ed.render(ed.parentEl || document.body);
39787                 }
39788                 ed.field.reset();
39789                
39790                 cell.hide();
39791                 
39792                 (function(){ // complex but required for focus issues in safari, ie and opera
39793                     ed.row = row;
39794                     ed.col = col;
39795                     ed.record = r;
39796                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39797                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39798                     this.activeEditor = ed;
39799                     var v = r.data[field];
39800                     ed.startEdit(this.view.getCell(row, col), v);
39801                     // combo's with 'displayField and name set
39802                     if (ed.field.displayField && ed.field.name) {
39803                         ed.field.el.dom.value = r.data[ed.field.name];
39804                     }
39805                     
39806                     
39807                 }).defer(50, this);
39808             }
39809         }
39810     },
39811         
39812     /**
39813      * Stops any active editing
39814      */
39815     stopEditing : function(){
39816         if(this.activeEditor){
39817             this.activeEditor.completeEdit();
39818         }
39819         this.activeEditor = null;
39820     },
39821         
39822          /**
39823      * Called to get grid's drag proxy text, by default returns this.ddText.
39824      * @return {String}
39825      */
39826     getDragDropText : function(){
39827         var count = this.selModel.getSelectedCell() ? 1 : 0;
39828         return String.format(this.ddText, count, count == 1 ? '' : 's');
39829     }
39830         
39831 });/*
39832  * Based on:
39833  * Ext JS Library 1.1.1
39834  * Copyright(c) 2006-2007, Ext JS, LLC.
39835  *
39836  * Originally Released Under LGPL - original licence link has changed is not relivant.
39837  *
39838  * Fork - LGPL
39839  * <script type="text/javascript">
39840  */
39841
39842 // private - not really -- you end up using it !
39843 // This is a support class used internally by the Grid components
39844
39845 /**
39846  * @class Roo.grid.GridEditor
39847  * @extends Roo.Editor
39848  * Class for creating and editable grid elements.
39849  * @param {Object} config any settings (must include field)
39850  */
39851 Roo.grid.GridEditor = function(field, config){
39852     if (!config && field.field) {
39853         config = field;
39854         field = Roo.factory(config.field, Roo.form);
39855     }
39856     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39857     field.monitorTab = false;
39858 };
39859
39860 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39861     
39862     /**
39863      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39864      */
39865     
39866     alignment: "tl-tl",
39867     autoSize: "width",
39868     hideEl : false,
39869     cls: "x-small-editor x-grid-editor",
39870     shim:false,
39871     shadow:"frame"
39872 });/*
39873  * Based on:
39874  * Ext JS Library 1.1.1
39875  * Copyright(c) 2006-2007, Ext JS, LLC.
39876  *
39877  * Originally Released Under LGPL - original licence link has changed is not relivant.
39878  *
39879  * Fork - LGPL
39880  * <script type="text/javascript">
39881  */
39882   
39883
39884   
39885 Roo.grid.PropertyRecord = Roo.data.Record.create([
39886     {name:'name',type:'string'},  'value'
39887 ]);
39888
39889
39890 Roo.grid.PropertyStore = function(grid, source){
39891     this.grid = grid;
39892     this.store = new Roo.data.Store({
39893         recordType : Roo.grid.PropertyRecord
39894     });
39895     this.store.on('update', this.onUpdate,  this);
39896     if(source){
39897         this.setSource(source);
39898     }
39899     Roo.grid.PropertyStore.superclass.constructor.call(this);
39900 };
39901
39902
39903
39904 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39905     setSource : function(o){
39906         this.source = o;
39907         this.store.removeAll();
39908         var data = [];
39909         for(var k in o){
39910             if(this.isEditableValue(o[k])){
39911                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39912             }
39913         }
39914         this.store.loadRecords({records: data}, {}, true);
39915     },
39916
39917     onUpdate : function(ds, record, type){
39918         if(type == Roo.data.Record.EDIT){
39919             var v = record.data['value'];
39920             var oldValue = record.modified['value'];
39921             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39922                 this.source[record.id] = v;
39923                 record.commit();
39924                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39925             }else{
39926                 record.reject();
39927             }
39928         }
39929     },
39930
39931     getProperty : function(row){
39932        return this.store.getAt(row);
39933     },
39934
39935     isEditableValue: function(val){
39936         if(val && val instanceof Date){
39937             return true;
39938         }else if(typeof val == 'object' || typeof val == 'function'){
39939             return false;
39940         }
39941         return true;
39942     },
39943
39944     setValue : function(prop, value){
39945         this.source[prop] = value;
39946         this.store.getById(prop).set('value', value);
39947     },
39948
39949     getSource : function(){
39950         return this.source;
39951     }
39952 });
39953
39954 Roo.grid.PropertyColumnModel = function(grid, store){
39955     this.grid = grid;
39956     var g = Roo.grid;
39957     g.PropertyColumnModel.superclass.constructor.call(this, [
39958         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39959         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39960     ]);
39961     this.store = store;
39962     this.bselect = Roo.DomHelper.append(document.body, {
39963         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39964             {tag: 'option', value: 'true', html: 'true'},
39965             {tag: 'option', value: 'false', html: 'false'}
39966         ]
39967     });
39968     Roo.id(this.bselect);
39969     var f = Roo.form;
39970     this.editors = {
39971         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39972         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39973         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39974         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39975         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39976     };
39977     this.renderCellDelegate = this.renderCell.createDelegate(this);
39978     this.renderPropDelegate = this.renderProp.createDelegate(this);
39979 };
39980
39981 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39982     
39983     
39984     nameText : 'Name',
39985     valueText : 'Value',
39986     
39987     dateFormat : 'm/j/Y',
39988     
39989     
39990     renderDate : function(dateVal){
39991         return dateVal.dateFormat(this.dateFormat);
39992     },
39993
39994     renderBool : function(bVal){
39995         return bVal ? 'true' : 'false';
39996     },
39997
39998     isCellEditable : function(colIndex, rowIndex){
39999         return colIndex == 1;
40000     },
40001
40002     getRenderer : function(col){
40003         return col == 1 ?
40004             this.renderCellDelegate : this.renderPropDelegate;
40005     },
40006
40007     renderProp : function(v){
40008         return this.getPropertyName(v);
40009     },
40010
40011     renderCell : function(val){
40012         var rv = val;
40013         if(val instanceof Date){
40014             rv = this.renderDate(val);
40015         }else if(typeof val == 'boolean'){
40016             rv = this.renderBool(val);
40017         }
40018         return Roo.util.Format.htmlEncode(rv);
40019     },
40020
40021     getPropertyName : function(name){
40022         var pn = this.grid.propertyNames;
40023         return pn && pn[name] ? pn[name] : name;
40024     },
40025
40026     getCellEditor : function(colIndex, rowIndex){
40027         var p = this.store.getProperty(rowIndex);
40028         var n = p.data['name'], val = p.data['value'];
40029         
40030         if(typeof(this.grid.customEditors[n]) == 'string'){
40031             return this.editors[this.grid.customEditors[n]];
40032         }
40033         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40034             return this.grid.customEditors[n];
40035         }
40036         if(val instanceof Date){
40037             return this.editors['date'];
40038         }else if(typeof val == 'number'){
40039             return this.editors['number'];
40040         }else if(typeof val == 'boolean'){
40041             return this.editors['boolean'];
40042         }else{
40043             return this.editors['string'];
40044         }
40045     }
40046 });
40047
40048 /**
40049  * @class Roo.grid.PropertyGrid
40050  * @extends Roo.grid.EditorGrid
40051  * This class represents the  interface of a component based property grid control.
40052  * <br><br>Usage:<pre><code>
40053  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40054       
40055  });
40056  // set any options
40057  grid.render();
40058  * </code></pre>
40059   
40060  * @constructor
40061  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40062  * The container MUST have some type of size defined for the grid to fill. The container will be
40063  * automatically set to position relative if it isn't already.
40064  * @param {Object} config A config object that sets properties on this grid.
40065  */
40066 Roo.grid.PropertyGrid = function(container, config){
40067     config = config || {};
40068     var store = new Roo.grid.PropertyStore(this);
40069     this.store = store;
40070     var cm = new Roo.grid.PropertyColumnModel(this, store);
40071     store.store.sort('name', 'ASC');
40072     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40073         ds: store.store,
40074         cm: cm,
40075         enableColLock:false,
40076         enableColumnMove:false,
40077         stripeRows:false,
40078         trackMouseOver: false,
40079         clicksToEdit:1
40080     }, config));
40081     this.getGridEl().addClass('x-props-grid');
40082     this.lastEditRow = null;
40083     this.on('columnresize', this.onColumnResize, this);
40084     this.addEvents({
40085          /**
40086              * @event beforepropertychange
40087              * Fires before a property changes (return false to stop?)
40088              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40089              * @param {String} id Record Id
40090              * @param {String} newval New Value
40091          * @param {String} oldval Old Value
40092              */
40093         "beforepropertychange": true,
40094         /**
40095              * @event propertychange
40096              * Fires after a property changes
40097              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40098              * @param {String} id Record Id
40099              * @param {String} newval New Value
40100          * @param {String} oldval Old Value
40101              */
40102         "propertychange": true
40103     });
40104     this.customEditors = this.customEditors || {};
40105 };
40106 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40107     
40108      /**
40109      * @cfg {Object} customEditors map of colnames=> custom editors.
40110      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40111      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40112      * false disables editing of the field.
40113          */
40114     
40115       /**
40116      * @cfg {Object} propertyNames map of property Names to their displayed value
40117          */
40118     
40119     render : function(){
40120         Roo.grid.PropertyGrid.superclass.render.call(this);
40121         this.autoSize.defer(100, this);
40122     },
40123
40124     autoSize : function(){
40125         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40126         if(this.view){
40127             this.view.fitColumns();
40128         }
40129     },
40130
40131     onColumnResize : function(){
40132         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40133         this.autoSize();
40134     },
40135     /**
40136      * Sets the data for the Grid
40137      * accepts a Key => Value object of all the elements avaiable.
40138      * @param {Object} data  to appear in grid.
40139      */
40140     setSource : function(source){
40141         this.store.setSource(source);
40142         //this.autoSize();
40143     },
40144     /**
40145      * Gets all the data from the grid.
40146      * @return {Object} data  data stored in grid
40147      */
40148     getSource : function(){
40149         return this.store.getSource();
40150     }
40151 });/*
40152  * Based on:
40153  * Ext JS Library 1.1.1
40154  * Copyright(c) 2006-2007, Ext JS, LLC.
40155  *
40156  * Originally Released Under LGPL - original licence link has changed is not relivant.
40157  *
40158  * Fork - LGPL
40159  * <script type="text/javascript">
40160  */
40161  
40162 /**
40163  * @class Roo.LoadMask
40164  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40165  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40166  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40167  * element's UpdateManager load indicator and will be destroyed after the initial load.
40168  * @constructor
40169  * Create a new LoadMask
40170  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40171  * @param {Object} config The config object
40172  */
40173 Roo.LoadMask = function(el, config){
40174     this.el = Roo.get(el);
40175     Roo.apply(this, config);
40176     if(this.store){
40177         this.store.on('beforeload', this.onBeforeLoad, this);
40178         this.store.on('load', this.onLoad, this);
40179         this.store.on('loadexception', this.onLoadException, this);
40180         this.removeMask = false;
40181     }else{
40182         var um = this.el.getUpdateManager();
40183         um.showLoadIndicator = false; // disable the default indicator
40184         um.on('beforeupdate', this.onBeforeLoad, this);
40185         um.on('update', this.onLoad, this);
40186         um.on('failure', this.onLoad, this);
40187         this.removeMask = true;
40188     }
40189 };
40190
40191 Roo.LoadMask.prototype = {
40192     /**
40193      * @cfg {Boolean} removeMask
40194      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40195      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40196      */
40197     /**
40198      * @cfg {String} msg
40199      * The text to display in a centered loading message box (defaults to 'Loading...')
40200      */
40201     msg : 'Loading...',
40202     /**
40203      * @cfg {String} msgCls
40204      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40205      */
40206     msgCls : 'x-mask-loading',
40207
40208     /**
40209      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40210      * @type Boolean
40211      */
40212     disabled: false,
40213
40214     /**
40215      * Disables the mask to prevent it from being displayed
40216      */
40217     disable : function(){
40218        this.disabled = true;
40219     },
40220
40221     /**
40222      * Enables the mask so that it can be displayed
40223      */
40224     enable : function(){
40225         this.disabled = false;
40226     },
40227     
40228     onLoadException : function()
40229     {
40230         Roo.log(arguments);
40231         
40232         if (typeof(arguments[3]) != 'undefined') {
40233             Roo.MessageBox.alert("Error loading",arguments[3]);
40234         } 
40235         /*
40236         try {
40237             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40238                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40239             }   
40240         } catch(e) {
40241             
40242         }
40243         */
40244     
40245         
40246         
40247         this.el.unmask(this.removeMask);
40248     },
40249     // private
40250     onLoad : function()
40251     {
40252         this.el.unmask(this.removeMask);
40253     },
40254
40255     // private
40256     onBeforeLoad : function(){
40257         if(!this.disabled){
40258             this.el.mask(this.msg, this.msgCls);
40259         }
40260     },
40261
40262     // private
40263     destroy : function(){
40264         if(this.store){
40265             this.store.un('beforeload', this.onBeforeLoad, this);
40266             this.store.un('load', this.onLoad, this);
40267             this.store.un('loadexception', this.onLoadException, this);
40268         }else{
40269             var um = this.el.getUpdateManager();
40270             um.un('beforeupdate', this.onBeforeLoad, this);
40271             um.un('update', this.onLoad, this);
40272             um.un('failure', this.onLoad, this);
40273         }
40274     }
40275 };/*
40276  * Based on:
40277  * Ext JS Library 1.1.1
40278  * Copyright(c) 2006-2007, Ext JS, LLC.
40279  *
40280  * Originally Released Under LGPL - original licence link has changed is not relivant.
40281  *
40282  * Fork - LGPL
40283  * <script type="text/javascript">
40284  */
40285
40286
40287 /**
40288  * @class Roo.XTemplate
40289  * @extends Roo.Template
40290  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40291 <pre><code>
40292 var t = new Roo.XTemplate(
40293         '&lt;select name="{name}"&gt;',
40294                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40295         '&lt;/select&gt;'
40296 );
40297  
40298 // then append, applying the master template values
40299  </code></pre>
40300  *
40301  * Supported features:
40302  *
40303  *  Tags:
40304
40305 <pre><code>
40306       {a_variable} - output encoded.
40307       {a_variable.format:("Y-m-d")} - call a method on the variable
40308       {a_variable:raw} - unencoded output
40309       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40310       {a_variable:this.method_on_template(...)} - call a method on the template object.
40311  
40312 </code></pre>
40313  *  The tpl tag:
40314 <pre><code>
40315         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40316         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40317         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40318         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40319   
40320         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40321         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40322 </code></pre>
40323  *      
40324  */
40325 Roo.XTemplate = function()
40326 {
40327     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40328     if (this.html) {
40329         this.compile();
40330     }
40331 };
40332
40333
40334 Roo.extend(Roo.XTemplate, Roo.Template, {
40335
40336     /**
40337      * The various sub templates
40338      */
40339     tpls : false,
40340     /**
40341      *
40342      * basic tag replacing syntax
40343      * WORD:WORD()
40344      *
40345      * // you can fake an object call by doing this
40346      *  x.t:(test,tesT) 
40347      * 
40348      */
40349     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40350
40351     /**
40352      * compile the template
40353      *
40354      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40355      *
40356      */
40357     compile: function()
40358     {
40359         var s = this.html;
40360      
40361         s = ['<tpl>', s, '</tpl>'].join('');
40362     
40363         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40364             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40365             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40366             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40367             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40368             m,
40369             id     = 0,
40370             tpls   = [];
40371     
40372         while(true == !!(m = s.match(re))){
40373             var forMatch   = m[0].match(nameRe),
40374                 ifMatch   = m[0].match(ifRe),
40375                 execMatch   = m[0].match(execRe),
40376                 namedMatch   = m[0].match(namedRe),
40377                 
40378                 exp  = null, 
40379                 fn   = null,
40380                 exec = null,
40381                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40382                 
40383             if (ifMatch) {
40384                 // if - puts fn into test..
40385                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40386                 if(exp){
40387                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40388                 }
40389             }
40390             
40391             if (execMatch) {
40392                 // exec - calls a function... returns empty if true is  returned.
40393                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40394                 if(exp){
40395                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40396                 }
40397             }
40398             
40399             
40400             if (name) {
40401                 // for = 
40402                 switch(name){
40403                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40404                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40405                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40406                 }
40407             }
40408             var uid = namedMatch ? namedMatch[1] : id;
40409             
40410             
40411             tpls.push({
40412                 id:     namedMatch ? namedMatch[1] : id,
40413                 target: name,
40414                 exec:   exec,
40415                 test:   fn,
40416                 body:   m[1] || ''
40417             });
40418             if (namedMatch) {
40419                 s = s.replace(m[0], '');
40420             } else { 
40421                 s = s.replace(m[0], '{xtpl'+ id + '}');
40422             }
40423             ++id;
40424         }
40425         this.tpls = [];
40426         for(var i = tpls.length-1; i >= 0; --i){
40427             this.compileTpl(tpls[i]);
40428             this.tpls[tpls[i].id] = tpls[i];
40429         }
40430         this.master = tpls[tpls.length-1];
40431         return this;
40432     },
40433     /**
40434      * same as applyTemplate, except it's done to one of the subTemplates
40435      * when using named templates, you can do:
40436      *
40437      * var str = pl.applySubTemplate('your-name', values);
40438      *
40439      * 
40440      * @param {Number} id of the template
40441      * @param {Object} values to apply to template
40442      * @param {Object} parent (normaly the instance of this object)
40443      */
40444     applySubTemplate : function(id, values, parent)
40445     {
40446         
40447         
40448         var t = this.tpls[id];
40449         
40450         
40451         try { 
40452             if(t.test && !t.test.call(this, values, parent)){
40453                 return '';
40454             }
40455         } catch(e) {
40456             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40457             Roo.log(e.toString());
40458             Roo.log(t.test);
40459             return ''
40460         }
40461         try { 
40462             
40463             if(t.exec && t.exec.call(this, values, parent)){
40464                 return '';
40465             }
40466         } catch(e) {
40467             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40468             Roo.log(e.toString());
40469             Roo.log(t.exec);
40470             return ''
40471         }
40472         try {
40473             var vs = t.target ? t.target.call(this, values, parent) : values;
40474             parent = t.target ? values : parent;
40475             if(t.target && vs instanceof Array){
40476                 var buf = [];
40477                 for(var i = 0, len = vs.length; i < len; i++){
40478                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40479                 }
40480                 return buf.join('');
40481             }
40482             return t.compiled.call(this, vs, parent);
40483         } catch (e) {
40484             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40485             Roo.log(e.toString());
40486             Roo.log(t.compiled);
40487             return '';
40488         }
40489     },
40490
40491     compileTpl : function(tpl)
40492     {
40493         var fm = Roo.util.Format;
40494         var useF = this.disableFormats !== true;
40495         var sep = Roo.isGecko ? "+" : ",";
40496         var undef = function(str) {
40497             Roo.log("Property not found :"  + str);
40498             return '';
40499         };
40500         
40501         var fn = function(m, name, format, args)
40502         {
40503             //Roo.log(arguments);
40504             args = args ? args.replace(/\\'/g,"'") : args;
40505             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40506             if (typeof(format) == 'undefined') {
40507                 format= 'htmlEncode';
40508             }
40509             if (format == 'raw' ) {
40510                 format = false;
40511             }
40512             
40513             if(name.substr(0, 4) == 'xtpl'){
40514                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40515             }
40516             
40517             // build an array of options to determine if value is undefined..
40518             
40519             // basically get 'xxxx.yyyy' then do
40520             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40521             //    (function () { Roo.log("Property not found"); return ''; })() :
40522             //    ......
40523             
40524             var udef_ar = [];
40525             var lookfor = '';
40526             Roo.each(name.split('.'), function(st) {
40527                 lookfor += (lookfor.length ? '.': '') + st;
40528                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40529             });
40530             
40531             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40532             
40533             
40534             if(format && useF){
40535                 
40536                 args = args ? ',' + args : "";
40537                  
40538                 if(format.substr(0, 5) != "this."){
40539                     format = "fm." + format + '(';
40540                 }else{
40541                     format = 'this.call("'+ format.substr(5) + '", ';
40542                     args = ", values";
40543                 }
40544                 
40545                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40546             }
40547              
40548             if (args.length) {
40549                 // called with xxyx.yuu:(test,test)
40550                 // change to ()
40551                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40552             }
40553             // raw.. - :raw modifier..
40554             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40555             
40556         };
40557         var body;
40558         // branched to use + in gecko and [].join() in others
40559         if(Roo.isGecko){
40560             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40561                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40562                     "';};};";
40563         }else{
40564             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40565             body.push(tpl.body.replace(/(\r\n|\n)/g,
40566                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40567             body.push("'].join('');};};");
40568             body = body.join('');
40569         }
40570         
40571         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40572        
40573         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40574         eval(body);
40575         
40576         return this;
40577     },
40578
40579     applyTemplate : function(values){
40580         return this.master.compiled.call(this, values, {});
40581         //var s = this.subs;
40582     },
40583
40584     apply : function(){
40585         return this.applyTemplate.apply(this, arguments);
40586     }
40587
40588  });
40589
40590 Roo.XTemplate.from = function(el){
40591     el = Roo.getDom(el);
40592     return new Roo.XTemplate(el.value || el.innerHTML);
40593 };/*
40594  * Original code for Roojs - LGPL
40595  * <script type="text/javascript">
40596  */
40597  
40598 /**
40599  * @class Roo.XComponent
40600  * A delayed Element creator...
40601  * Or a way to group chunks of interface together.
40602  * 
40603  * Mypart.xyx = new Roo.XComponent({
40604
40605     parent : 'Mypart.xyz', // empty == document.element.!!
40606     order : '001',
40607     name : 'xxxx'
40608     region : 'xxxx'
40609     disabled : function() {} 
40610      
40611     tree : function() { // return an tree of xtype declared components
40612         var MODULE = this;
40613         return 
40614         {
40615             xtype : 'NestedLayoutPanel',
40616             // technicall
40617         }
40618      ]
40619  *})
40620  *
40621  *
40622  * It can be used to build a big heiracy, with parent etc.
40623  * or you can just use this to render a single compoent to a dom element
40624  * MYPART.render(Roo.Element | String(id) | dom_element )
40625  * 
40626  * @extends Roo.util.Observable
40627  * @constructor
40628  * @param cfg {Object} configuration of component
40629  * 
40630  */
40631 Roo.XComponent = function(cfg) {
40632     Roo.apply(this, cfg);
40633     this.addEvents({ 
40634         /**
40635              * @event built
40636              * Fires when this the componnt is built
40637              * @param {Roo.XComponent} c the component
40638              */
40639         'built' : true
40640         
40641     });
40642     this.region = this.region || 'center'; // default..
40643     Roo.XComponent.register(this);
40644     this.modules = false;
40645     this.el = false; // where the layout goes..
40646     
40647     
40648 }
40649 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40650     /**
40651      * @property el
40652      * The created element (with Roo.factory())
40653      * @type {Roo.Layout}
40654      */
40655     el  : false,
40656     
40657     /**
40658      * @property el
40659      * for BC  - use el in new code
40660      * @type {Roo.Layout}
40661      */
40662     panel : false,
40663     
40664     /**
40665      * @property layout
40666      * for BC  - use el in new code
40667      * @type {Roo.Layout}
40668      */
40669     layout : false,
40670     
40671      /**
40672      * @cfg {Function|boolean} disabled
40673      * If this module is disabled by some rule, return true from the funtion
40674      */
40675     disabled : false,
40676     
40677     /**
40678      * @cfg {String} parent 
40679      * Name of parent element which it get xtype added to..
40680      */
40681     parent: false,
40682     
40683     /**
40684      * @cfg {String} order
40685      * Used to set the order in which elements are created (usefull for multiple tabs)
40686      */
40687     
40688     order : false,
40689     /**
40690      * @cfg {String} name
40691      * String to display while loading.
40692      */
40693     name : false,
40694     /**
40695      * @cfg {String} region
40696      * Region to render component to (defaults to center)
40697      */
40698     region : 'center',
40699     
40700     /**
40701      * @cfg {Array} items
40702      * A single item array - the first element is the root of the tree..
40703      * It's done this way to stay compatible with the Xtype system...
40704      */
40705     items : false,
40706     
40707     /**
40708      * @property _tree
40709      * The method that retuns the tree of parts that make up this compoennt 
40710      * @type {function}
40711      */
40712     _tree  : false,
40713     
40714      /**
40715      * render
40716      * render element to dom or tree
40717      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40718      */
40719     
40720     render : function(el)
40721     {
40722         
40723         el = el || false;
40724         var hp = this.parent ? 1 : 0;
40725         
40726         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40727             // if parent is a '#.....' string, then let's use that..
40728             var ename = this.parent.substr(1)
40729             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
40730             el = Roo.get(ename);
40731             if (!el && !this.parent) {
40732                 Roo.log("Warning - element can not be found :#" + ename );
40733                 return;
40734             }
40735         }
40736         
40737         
40738         if (!this.parent) {
40739             
40740             el = el ? Roo.get(el) : false;      
40741             
40742             // it's a top level one..
40743             this.parent =  {
40744                 el : new Roo.BorderLayout(el || document.body, {
40745                 
40746                      center: {
40747                          titlebar: false,
40748                          autoScroll:false,
40749                          closeOnTab: true,
40750                          tabPosition: 'top',
40751                           //resizeTabs: true,
40752                          alwaysShowTabs: el && hp? false :  true,
40753                          hideTabs: el || !hp ? true :  false,
40754                          minTabWidth: 140
40755                      }
40756                  })
40757             }
40758         }
40759         
40760                 if (!this.parent.el) {
40761                         // probably an old style ctor, which has been disabled.
40762                         return;
40763                         
40764                 }
40765                 // The 'tree' method is  '_tree now' 
40766             
40767         var tree = this._tree ? this._tree() : this.tree();
40768         tree.region = tree.region || this.region;
40769         this.el = this.parent.el.addxtype(tree);
40770         this.fireEvent('built', this);
40771         
40772         this.panel = this.el;
40773         this.layout = this.panel.layout;
40774                 this.parentLayout = this.parent.layout  || false;  
40775          
40776     }
40777     
40778 });
40779
40780 Roo.apply(Roo.XComponent, {
40781     /**
40782      * @property  hideProgress
40783      * true to disable the building progress bar.. usefull on single page renders.
40784      * @type Boolean
40785      */
40786     hideProgress : false,
40787     /**
40788      * @property  buildCompleted
40789      * True when the builder has completed building the interface.
40790      * @type Boolean
40791      */
40792     buildCompleted : false,
40793      
40794     /**
40795      * @property  topModule
40796      * the upper most module - uses document.element as it's constructor.
40797      * @type Object
40798      */
40799      
40800     topModule  : false,
40801       
40802     /**
40803      * @property  modules
40804      * array of modules to be created by registration system.
40805      * @type {Array} of Roo.XComponent
40806      */
40807     
40808     modules : [],
40809     /**
40810      * @property  elmodules
40811      * array of modules to be created by which use #ID 
40812      * @type {Array} of Roo.XComponent
40813      */
40814      
40815     elmodules : [],
40816
40817     
40818     /**
40819      * Register components to be built later.
40820      *
40821      * This solves the following issues
40822      * - Building is not done on page load, but after an authentication process has occured.
40823      * - Interface elements are registered on page load
40824      * - Parent Interface elements may not be loaded before child, so this handles that..
40825      * 
40826      *
40827      * example:
40828      * 
40829      * MyApp.register({
40830           order : '000001',
40831           module : 'Pman.Tab.projectMgr',
40832           region : 'center',
40833           parent : 'Pman.layout',
40834           disabled : false,  // or use a function..
40835         })
40836      
40837      * * @param {Object} details about module
40838      */
40839     register : function(obj) {
40840                 
40841         Roo.XComponent.event.fireEvent('register', obj);
40842         switch(typeof(obj.disabled) ) {
40843                 
40844             case 'undefined':
40845                 break;
40846             
40847             case 'function':
40848                 if ( obj.disabled() ) {
40849                         return;
40850                 }
40851                 break;
40852             
40853             default:
40854                 if (obj.disabled) {
40855                         return;
40856                 }
40857                 break;
40858         }
40859                 
40860         this.modules.push(obj);
40861          
40862     },
40863     /**
40864      * convert a string to an object..
40865      * eg. 'AAA.BBB' -> finds AAA.BBB
40866
40867      */
40868     
40869     toObject : function(str)
40870     {
40871         if (!str || typeof(str) == 'object') {
40872             return str;
40873         }
40874         if (str.substring(0,1) == '#') {
40875             return str;
40876         }
40877
40878         var ar = str.split('.');
40879         var rt, o;
40880         rt = ar.shift();
40881             /** eval:var:o */
40882         try {
40883             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40884         } catch (e) {
40885             throw "Module not found : " + str;
40886         }
40887         
40888         if (o === false) {
40889             throw "Module not found : " + str;
40890         }
40891         Roo.each(ar, function(e) {
40892             if (typeof(o[e]) == 'undefined') {
40893                 throw "Module not found : " + str;
40894             }
40895             o = o[e];
40896         });
40897         
40898         return o;
40899         
40900     },
40901     
40902     
40903     /**
40904      * move modules into their correct place in the tree..
40905      * 
40906      */
40907     preBuild : function ()
40908     {
40909         var _t = this;
40910         Roo.each(this.modules , function (obj)
40911         {
40912             Roo.XComponent.event.fireEvent('beforebuild', obj);
40913             
40914             var opar = obj.parent;
40915             try { 
40916                 obj.parent = this.toObject(opar);
40917             } catch(e) {
40918                 Roo.log("parent:toObject failed: " + e.toString());
40919                 return;
40920             }
40921             
40922             if (!obj.parent) {
40923                 Roo.debug && Roo.log("GOT top level module");
40924                 Roo.debug && Roo.log(obj);
40925                 obj.modules = new Roo.util.MixedCollection(false, 
40926                     function(o) { return o.order + '' }
40927                 );
40928                 this.topModule = obj;
40929                 return;
40930             }
40931                         // parent is a string (usually a dom element name..)
40932             if (typeof(obj.parent) == 'string') {
40933                 this.elmodules.push(obj);
40934                 return;
40935             }
40936             if (obj.parent.constructor != Roo.XComponent) {
40937                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40938             }
40939             if (!obj.parent.modules) {
40940                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40941                     function(o) { return o.order + '' }
40942                 );
40943             }
40944             if (obj.parent.disabled) {
40945                 obj.disabled = true;
40946             }
40947             obj.parent.modules.add(obj);
40948         }, this);
40949     },
40950     
40951      /**
40952      * make a list of modules to build.
40953      * @return {Array} list of modules. 
40954      */ 
40955     
40956     buildOrder : function()
40957     {
40958         var _this = this;
40959         var cmp = function(a,b) {   
40960             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40961         };
40962         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40963             throw "No top level modules to build";
40964         }
40965         
40966         // make a flat list in order of modules to build.
40967         var mods = this.topModule ? [ this.topModule ] : [];
40968                 
40969         
40970         // elmodules (is a list of DOM based modules )
40971         Roo.each(this.elmodules, function(e) {
40972             mods.push(e);
40973             if (!this.topModule &&
40974                 typeof(e.parent) == 'string' &&
40975                 e.parent.substring(0,1) == '#' &&
40976                 Roo.get(e.parent.substr(1))
40977                ) {
40978                 
40979                 _this.topModule = e;
40980             }
40981             
40982         });
40983
40984         
40985         // add modules to their parents..
40986         var addMod = function(m) {
40987             Roo.debug && Roo.log("build Order: add: " + m.name);
40988                 
40989             mods.push(m);
40990             if (m.modules && !m.disabled) {
40991                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40992                 m.modules.keySort('ASC',  cmp );
40993                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40994     
40995                 m.modules.each(addMod);
40996             } else {
40997                 Roo.debug && Roo.log("build Order: no child modules");
40998             }
40999             // not sure if this is used any more..
41000             if (m.finalize) {
41001                 m.finalize.name = m.name + " (clean up) ";
41002                 mods.push(m.finalize);
41003             }
41004             
41005         }
41006         if (this.topModule && this.topModule.modules) { 
41007             this.topModule.modules.keySort('ASC',  cmp );
41008             this.topModule.modules.each(addMod);
41009         } 
41010         return mods;
41011     },
41012     
41013      /**
41014      * Build the registered modules.
41015      * @param {Object} parent element.
41016      * @param {Function} optional method to call after module has been added.
41017      * 
41018      */ 
41019    
41020     build : function() 
41021     {
41022         
41023         this.preBuild();
41024         var mods = this.buildOrder();
41025       
41026         //this.allmods = mods;
41027         //Roo.debug && Roo.log(mods);
41028         //return;
41029         if (!mods.length) { // should not happen
41030             throw "NO modules!!!";
41031         }
41032         
41033         
41034         var msg = "Building Interface...";
41035         // flash it up as modal - so we store the mask!?
41036         if (!this.hideProgress) {
41037             Roo.MessageBox.show({ title: 'loading' });
41038             Roo.MessageBox.show({
41039                title: "Please wait...",
41040                msg: msg,
41041                width:450,
41042                progress:true,
41043                closable:false,
41044                modal: false
41045               
41046             });
41047         }
41048         var total = mods.length;
41049         
41050         var _this = this;
41051         var progressRun = function() {
41052             if (!mods.length) {
41053                 Roo.debug && Roo.log('hide?');
41054                 if (!this.hideProgress) {
41055                     Roo.MessageBox.hide();
41056                 }
41057                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41058                 
41059                 // THE END...
41060                 return false;   
41061             }
41062             
41063             var m = mods.shift();
41064             
41065             
41066             Roo.debug && Roo.log(m);
41067             // not sure if this is supported any more.. - modules that are are just function
41068             if (typeof(m) == 'function') { 
41069                 m.call(this);
41070                 return progressRun.defer(10, _this);
41071             } 
41072             
41073             
41074             msg = "Building Interface " + (total  - mods.length) + 
41075                     " of " + total + 
41076                     (m.name ? (' - ' + m.name) : '');
41077                         Roo.debug && Roo.log(msg);
41078             if (!this.hideProgress) { 
41079                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41080             }
41081             
41082          
41083             // is the module disabled?
41084             var disabled = (typeof(m.disabled) == 'function') ?
41085                 m.disabled.call(m.module.disabled) : m.disabled;    
41086             
41087             
41088             if (disabled) {
41089                 return progressRun(); // we do not update the display!
41090             }
41091             
41092             // now build 
41093             
41094                         
41095                         
41096             m.render();
41097             // it's 10 on top level, and 1 on others??? why...
41098             return progressRun.defer(10, _this);
41099              
41100         }
41101         progressRun.defer(1, _this);
41102      
41103         
41104         
41105     },
41106         
41107         
41108         /**
41109          * Event Object.
41110          *
41111          *
41112          */
41113         event: false, 
41114     /**
41115          * wrapper for event.on - aliased later..  
41116          * Typically use to register a event handler for register:
41117          *
41118          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41119          *
41120          */
41121     on : false
41122    
41123     
41124     
41125 });
41126
41127 Roo.XComponent.event = new Roo.util.Observable({
41128                 events : { 
41129                         /**
41130                          * @event register
41131                          * Fires when an Component is registered,
41132                          * set the disable property on the Component to stop registration.
41133                          * @param {Roo.XComponent} c the component being registerd.
41134                          * 
41135                          */
41136                         'register' : true,
41137             /**
41138                          * @event beforebuild
41139                          * Fires before each Component is built
41140                          * can be used to apply permissions.
41141                          * @param {Roo.XComponent} c the component being registerd.
41142                          * 
41143                          */
41144                         'beforebuild' : true,
41145                         /**
41146                          * @event buildcomplete
41147                          * Fires on the top level element when all elements have been built
41148                          * @param {Roo.XComponent} the top level component.
41149                          */
41150                         'buildcomplete' : true
41151                         
41152                 }
41153 });
41154
41155 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41156