Merge branch 'master' of http://git.roojs.com/roojs1
[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         href:  {
27359             title: "Href",
27360             width: 220
27361         } // border?
27362         
27363     },
27364     'TABLE' : {
27365         rows : {
27366             title: "Rows",
27367             width: 20
27368         },
27369         cols : {
27370             title: "Cols",
27371             width: 20
27372         },
27373         width : {
27374             title: "Width",
27375             width: 40
27376         },
27377         height : {
27378             title: "Height",
27379             width: 40
27380         },
27381         border : {
27382             title: "Border",
27383             width: 20
27384         }
27385     },
27386     'TD' : {
27387         width : {
27388             title: "Width",
27389             width: 40
27390         },
27391         height : {
27392             title: "Height",
27393             width: 40
27394         },   
27395         align: {
27396             title: "Align",
27397             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27398             width: 80
27399         },
27400         valign: {
27401             title: "Valign",
27402             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27403             width: 80
27404         },
27405         colspan: {
27406             title: "Colspan",
27407             width: 20
27408             
27409         },
27410          'font-family'  : {
27411             title : "Font",
27412             style : 'fontFamily',
27413             displayField: 'display',
27414             optname : 'font-family',
27415             width: 140
27416         }
27417     },
27418     'INPUT' : {
27419         name : {
27420             title: "name",
27421             width: 120
27422         },
27423         value : {
27424             title: "Value",
27425             width: 120
27426         },
27427         width : {
27428             title: "Width",
27429             width: 40
27430         }
27431     },
27432     'LABEL' : {
27433         'for' : {
27434             title: "For",
27435             width: 120
27436         }
27437     },
27438     'TEXTAREA' : {
27439           name : {
27440             title: "name",
27441             width: 120
27442         },
27443         rows : {
27444             title: "Rows",
27445             width: 20
27446         },
27447         cols : {
27448             title: "Cols",
27449             width: 20
27450         }
27451     },
27452     'SELECT' : {
27453         name : {
27454             title: "name",
27455             width: 120
27456         },
27457         selectoptions : {
27458             title: "Options",
27459             width: 200
27460         }
27461     },
27462     
27463     // should we really allow this??
27464     // should this just be 
27465     'BODY' : {
27466         title : {
27467             title: "Title",
27468             width: 200,
27469             disabled : true
27470         }
27471     },
27472     'SPAN' : {
27473         'font-family'  : {
27474             title : "Font",
27475             style : 'fontFamily',
27476             displayField: 'display',
27477             optname : 'font-family',
27478             width: 140
27479         }
27480     },
27481     'DIV' : {
27482         'font-family'  : {
27483             title : "Font",
27484             style : 'fontFamily',
27485             displayField: 'display',
27486             optname : 'font-family',
27487             width: 140
27488         }
27489     },
27490      'P' : {
27491         'font-family'  : {
27492             title : "Font",
27493             style : 'fontFamily',
27494             displayField: 'display',
27495             optname : 'font-family',
27496             width: 140
27497         }
27498     },
27499     
27500     '*' : {
27501         // empty..
27502     }
27503
27504 };
27505
27506 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27507 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27508
27509 Roo.form.HtmlEditor.ToolbarContext.options = {
27510         'font-family'  : [ 
27511                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27512                 [ 'Courier New', 'Courier New'],
27513                 [ 'Tahoma', 'Tahoma'],
27514                 [ 'Times New Roman,serif', 'Times'],
27515                 [ 'Verdana','Verdana' ]
27516         ]
27517 };
27518
27519 // fixme - these need to be configurable..
27520  
27521
27522 Roo.form.HtmlEditor.ToolbarContext.types
27523
27524
27525 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27526     
27527     tb: false,
27528     
27529     rendered: false,
27530     
27531     editor : false,
27532     /**
27533      * @cfg {Object} disable  List of toolbar elements to disable
27534          
27535      */
27536     disable : false,
27537     /**
27538      * @cfg {Object} styles List of styles 
27539      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27540      *
27541      * These must be defined in the page, so they get rendered correctly..
27542      * .headline { }
27543      * TD.underline { }
27544      * 
27545      */
27546     styles : false,
27547     
27548     options: false,
27549     
27550     toolbars : false,
27551     
27552     init : function(editor)
27553     {
27554         this.editor = editor;
27555         
27556         
27557         var fid = editor.frameId;
27558         var etb = this;
27559         function btn(id, toggle, handler){
27560             var xid = fid + '-'+ id ;
27561             return {
27562                 id : xid,
27563                 cmd : id,
27564                 cls : 'x-btn-icon x-edit-'+id,
27565                 enableToggle:toggle !== false,
27566                 scope: editor, // was editor...
27567                 handler:handler||editor.relayBtnCmd,
27568                 clickEvent:'mousedown',
27569                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27570                 tabIndex:-1
27571             };
27572         }
27573         // create a new element.
27574         var wdiv = editor.wrap.createChild({
27575                 tag: 'div'
27576             }, editor.wrap.dom.firstChild.nextSibling, true);
27577         
27578         // can we do this more than once??
27579         
27580          // stop form submits
27581       
27582  
27583         // disable everything...
27584         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27585         this.toolbars = {};
27586            
27587         for (var i in  ty) {
27588           
27589             this.toolbars[i] = this.buildToolbar(ty[i],i);
27590         }
27591         this.tb = this.toolbars.BODY;
27592         this.tb.el.show();
27593         this.buildFooter();
27594         this.footer.show();
27595         editor.on('hide', function( ) { this.footer.hide() }, this);
27596         editor.on('show', function( ) { this.footer.show() }, this);
27597         
27598          
27599         this.rendered = true;
27600         
27601         // the all the btns;
27602         editor.on('editorevent', this.updateToolbar, this);
27603         // other toolbars need to implement this..
27604         //editor.on('editmodechange', this.updateToolbar, this);
27605     },
27606     
27607     
27608     
27609     /**
27610      * Protected method that will not generally be called directly. It triggers
27611      * a toolbar update by reading the markup state of the current selection in the editor.
27612      */
27613     updateToolbar: function(editor,ev,sel){
27614
27615         //Roo.log(ev);
27616         // capture mouse up - this is handy for selecting images..
27617         // perhaps should go somewhere else...
27618         if(!this.editor.activated){
27619              this.editor.onFirstFocus();
27620             return;
27621         }
27622         
27623         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27624         // selectNode - might want to handle IE?
27625         if (ev &&
27626             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27627             ev.target && ev.target.tagName == 'IMG') {
27628             // they have click on an image...
27629             // let's see if we can change the selection...
27630             sel = ev.target;
27631          
27632               var nodeRange = sel.ownerDocument.createRange();
27633             try {
27634                 nodeRange.selectNode(sel);
27635             } catch (e) {
27636                 nodeRange.selectNodeContents(sel);
27637             }
27638             //nodeRange.collapse(true);
27639             var s = editor.win.getSelection();
27640             s.removeAllRanges();
27641             s.addRange(nodeRange);
27642         }  
27643         
27644       
27645         var updateFooter = sel ? false : true;
27646         
27647         
27648         var ans = this.editor.getAllAncestors();
27649         
27650         // pick
27651         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27652         
27653         if (!sel) { 
27654             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27655             sel = sel ? sel : this.editor.doc.body;
27656             sel = sel.tagName.length ? sel : this.editor.doc.body;
27657             
27658         }
27659         // pick a menu that exists..
27660         var tn = sel.tagName.toUpperCase();
27661         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27662         
27663         tn = sel.tagName.toUpperCase();
27664         
27665         var lastSel = this.tb.selectedNode
27666         
27667         this.tb.selectedNode = sel;
27668         
27669         // if current menu does not match..
27670         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27671                 
27672             this.tb.el.hide();
27673             ///console.log("show: " + tn);
27674             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27675             this.tb.el.show();
27676             // update name
27677             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27678             
27679             
27680             // update attributes
27681             if (this.tb.fields) {
27682                 this.tb.fields.each(function(e) {
27683                     if (e.stylename) {
27684                         e.setValue(sel.style[e.stylename]);
27685                         return;
27686                     } 
27687                    e.setValue(sel.getAttribute(e.attrname));
27688                 });
27689             }
27690             
27691             var hasStyles = false;
27692             for(var i in this.styles) {
27693                 hasStyles = true;
27694                 break;
27695             }
27696             
27697             // update styles
27698             if (hasStyles) { 
27699                 var st = this.tb.fields.item(0);
27700                 
27701                 st.store.removeAll();
27702                
27703                 
27704                 var cn = sel.className.split(/\s+/);
27705                 
27706                 var avs = [];
27707                 if (this.styles['*']) {
27708                     
27709                     Roo.each(this.styles['*'], function(v) {
27710                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27711                     });
27712                 }
27713                 if (this.styles[tn]) { 
27714                     Roo.each(this.styles[tn], function(v) {
27715                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27716                     });
27717                 }
27718                 
27719                 st.store.loadData(avs);
27720                 st.collapse();
27721                 st.setValue(cn);
27722             }
27723             // flag our selected Node.
27724             this.tb.selectedNode = sel;
27725            
27726            
27727             Roo.menu.MenuMgr.hideAll();
27728
27729         }
27730         
27731         if (!updateFooter) {
27732             //this.footDisp.dom.innerHTML = ''; 
27733             return;
27734         }
27735         // update the footer
27736         //
27737         var html = '';
27738         
27739         this.footerEls = ans.reverse();
27740         Roo.each(this.footerEls, function(a,i) {
27741             if (!a) { return; }
27742             html += html.length ? ' &gt; '  :  '';
27743             
27744             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27745             
27746         });
27747        
27748         // 
27749         var sz = this.footDisp.up('td').getSize();
27750         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27751         this.footDisp.dom.style.marginLeft = '5px';
27752         
27753         this.footDisp.dom.style.overflow = 'hidden';
27754         
27755         this.footDisp.dom.innerHTML = html;
27756             
27757         //this.editorsyncValue();
27758     },
27759      
27760     
27761    
27762        
27763     // private
27764     onDestroy : function(){
27765         if(this.rendered){
27766             
27767             this.tb.items.each(function(item){
27768                 if(item.menu){
27769                     item.menu.removeAll();
27770                     if(item.menu.el){
27771                         item.menu.el.destroy();
27772                     }
27773                 }
27774                 item.destroy();
27775             });
27776              
27777         }
27778     },
27779     onFirstFocus: function() {
27780         // need to do this for all the toolbars..
27781         this.tb.items.each(function(item){
27782            item.enable();
27783         });
27784     },
27785     buildToolbar: function(tlist, nm)
27786     {
27787         var editor = this.editor;
27788          // create a new element.
27789         var wdiv = editor.wrap.createChild({
27790                 tag: 'div'
27791             }, editor.wrap.dom.firstChild.nextSibling, true);
27792         
27793        
27794         var tb = new Roo.Toolbar(wdiv);
27795         // add the name..
27796         
27797         tb.add(nm+ ":&nbsp;");
27798         
27799         var styles = [];
27800         for(var i in this.styles) {
27801             styles.push(i);
27802         }
27803         
27804         // styles...
27805         if (styles && styles.length) {
27806             
27807             // this needs a multi-select checkbox...
27808             tb.addField( new Roo.form.ComboBox({
27809                 store: new Roo.data.SimpleStore({
27810                     id : 'val',
27811                     fields: ['val', 'selected'],
27812                     data : [] 
27813                 }),
27814                 name : '-roo-edit-className',
27815                 attrname : 'className',
27816                 displayField: 'val',
27817                 typeAhead: false,
27818                 mode: 'local',
27819                 editable : false,
27820                 triggerAction: 'all',
27821                 emptyText:'Select Style',
27822                 selectOnFocus:true,
27823                 width: 130,
27824                 listeners : {
27825                     'select': function(c, r, i) {
27826                         // initial support only for on class per el..
27827                         tb.selectedNode.className =  r ? r.get('val') : '';
27828                         editor.syncValue();
27829                     }
27830                 }
27831     
27832             }));
27833         }
27834         
27835         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27836         var tbops = tbc.options;
27837         
27838         for (var i in tlist) {
27839             
27840             var item = tlist[i];
27841             tb.add(item.title + ":&nbsp;");
27842             
27843             
27844             //optname == used so you can configure the options available..
27845             var opts = item.opts ? item.opts : false;
27846             if (item.optname) {
27847                 opts = tbops[item.optname];
27848            
27849             }
27850             
27851             if (opts) {
27852                 // opts == pulldown..
27853                 tb.addField( new Roo.form.ComboBox({
27854                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27855                         id : 'val',
27856                         fields: ['val', 'display'],
27857                         data : opts  
27858                     }),
27859                     name : '-roo-edit-' + i,
27860                     attrname : i,
27861                     stylename : item.style ? item.style : false,
27862                     displayField: item.displayField ? item.displayField : 'val',
27863                     valueField :  'val',
27864                     typeAhead: false,
27865                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27866                     editable : false,
27867                     triggerAction: 'all',
27868                     emptyText:'Select',
27869                     selectOnFocus:true,
27870                     width: item.width ? item.width  : 130,
27871                     listeners : {
27872                         'select': function(c, r, i) {
27873                             if (c.stylename) {
27874                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27875                                 return;
27876                             }
27877                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27878                         }
27879                     }
27880
27881                 }));
27882                 continue;
27883                     
27884                  
27885                 
27886                 tb.addField( new Roo.form.TextField({
27887                     name: i,
27888                     width: 100,
27889                     //allowBlank:false,
27890                     value: ''
27891                 }));
27892                 continue;
27893             }
27894             tb.addField( new Roo.form.TextField({
27895                 name: '-roo-edit-' + i,
27896                 attrname : i,
27897                 
27898                 width: item.width,
27899                 //allowBlank:true,
27900                 value: '',
27901                 listeners: {
27902                     'change' : function(f, nv, ov) {
27903                         tb.selectedNode.setAttribute(f.attrname, nv);
27904                     }
27905                 }
27906             }));
27907              
27908         }
27909         tb.addFill();
27910         var _this = this;
27911         tb.addButton( {
27912             text: 'Remove Tag',
27913     
27914             listeners : {
27915                 click : function ()
27916                 {
27917                     // remove
27918                     // undo does not work.
27919                      
27920                     var sn = tb.selectedNode;
27921                     
27922                     var pn = sn.parentNode;
27923                     
27924                     var stn =  sn.childNodes[0];
27925                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27926                     while (sn.childNodes.length) {
27927                         var node = sn.childNodes[0];
27928                         sn.removeChild(node);
27929                         //Roo.log(node);
27930                         pn.insertBefore(node, sn);
27931                         
27932                     }
27933                     pn.removeChild(sn);
27934                     var range = editor.createRange();
27935         
27936                     range.setStart(stn,0);
27937                     range.setEnd(en,0); //????
27938                     //range.selectNode(sel);
27939                     
27940                     
27941                     var selection = editor.getSelection();
27942                     selection.removeAllRanges();
27943                     selection.addRange(range);
27944                     
27945                     
27946                     
27947                     //_this.updateToolbar(null, null, pn);
27948                     _this.updateToolbar(null, null, null);
27949                     _this.footDisp.dom.innerHTML = ''; 
27950                 }
27951             }
27952             
27953                     
27954                 
27955             
27956         });
27957         
27958         
27959         tb.el.on('click', function(e){
27960             e.preventDefault(); // what does this do?
27961         });
27962         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27963         tb.el.hide();
27964         tb.name = nm;
27965         // dont need to disable them... as they will get hidden
27966         return tb;
27967          
27968         
27969     },
27970     buildFooter : function()
27971     {
27972         
27973         var fel = this.editor.wrap.createChild();
27974         this.footer = new Roo.Toolbar(fel);
27975         // toolbar has scrolly on left / right?
27976         var footDisp= new Roo.Toolbar.Fill();
27977         var _t = this;
27978         this.footer.add(
27979             {
27980                 text : '&lt;',
27981                 xtype: 'Button',
27982                 handler : function() {
27983                     _t.footDisp.scrollTo('left',0,true)
27984                 }
27985             }
27986         );
27987         this.footer.add( footDisp );
27988         this.footer.add( 
27989             {
27990                 text : '&gt;',
27991                 xtype: 'Button',
27992                 handler : function() {
27993                     // no animation..
27994                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27995                 }
27996             }
27997         );
27998         var fel = Roo.get(footDisp.el);
27999         fel.addClass('x-editor-context');
28000         this.footDispWrap = fel; 
28001         this.footDispWrap.overflow  = 'hidden';
28002         
28003         this.footDisp = fel.createChild();
28004         this.footDispWrap.on('click', this.onContextClick, this)
28005         
28006         
28007     },
28008     onContextClick : function (ev,dom)
28009     {
28010         ev.preventDefault();
28011         var  cn = dom.className;
28012         //Roo.log(cn);
28013         if (!cn.match(/x-ed-loc-/)) {
28014             return;
28015         }
28016         var n = cn.split('-').pop();
28017         var ans = this.footerEls;
28018         var sel = ans[n];
28019         
28020          // pick
28021         var range = this.editor.createRange();
28022         
28023         range.selectNodeContents(sel);
28024         //range.selectNode(sel);
28025         
28026         
28027         var selection = this.editor.getSelection();
28028         selection.removeAllRanges();
28029         selection.addRange(range);
28030         
28031         
28032         
28033         this.updateToolbar(null, null, sel);
28034         
28035         
28036     }
28037     
28038     
28039     
28040     
28041     
28042 });
28043
28044
28045
28046
28047
28048 /*
28049  * Based on:
28050  * Ext JS Library 1.1.1
28051  * Copyright(c) 2006-2007, Ext JS, LLC.
28052  *
28053  * Originally Released Under LGPL - original licence link has changed is not relivant.
28054  *
28055  * Fork - LGPL
28056  * <script type="text/javascript">
28057  */
28058  
28059 /**
28060  * @class Roo.form.BasicForm
28061  * @extends Roo.util.Observable
28062  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28063  * @constructor
28064  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28065  * @param {Object} config Configuration options
28066  */
28067 Roo.form.BasicForm = function(el, config){
28068     this.allItems = [];
28069     this.childForms = [];
28070     Roo.apply(this, config);
28071     /*
28072      * The Roo.form.Field items in this form.
28073      * @type MixedCollection
28074      */
28075      
28076      
28077     this.items = new Roo.util.MixedCollection(false, function(o){
28078         return o.id || (o.id = Roo.id());
28079     });
28080     this.addEvents({
28081         /**
28082          * @event beforeaction
28083          * Fires before any action is performed. Return false to cancel the action.
28084          * @param {Form} this
28085          * @param {Action} action The action to be performed
28086          */
28087         beforeaction: true,
28088         /**
28089          * @event actionfailed
28090          * Fires when an action fails.
28091          * @param {Form} this
28092          * @param {Action} action The action that failed
28093          */
28094         actionfailed : true,
28095         /**
28096          * @event actioncomplete
28097          * Fires when an action is completed.
28098          * @param {Form} this
28099          * @param {Action} action The action that completed
28100          */
28101         actioncomplete : true
28102     });
28103     if(el){
28104         this.initEl(el);
28105     }
28106     Roo.form.BasicForm.superclass.constructor.call(this);
28107 };
28108
28109 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28110     /**
28111      * @cfg {String} method
28112      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28113      */
28114     /**
28115      * @cfg {DataReader} reader
28116      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28117      * This is optional as there is built-in support for processing JSON.
28118      */
28119     /**
28120      * @cfg {DataReader} errorReader
28121      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28122      * This is completely optional as there is built-in support for processing JSON.
28123      */
28124     /**
28125      * @cfg {String} url
28126      * The URL to use for form actions if one isn't supplied in the action options.
28127      */
28128     /**
28129      * @cfg {Boolean} fileUpload
28130      * Set to true if this form is a file upload.
28131      */
28132      
28133     /**
28134      * @cfg {Object} baseParams
28135      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28136      */
28137      /**
28138      
28139     /**
28140      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28141      */
28142     timeout: 30,
28143
28144     // private
28145     activeAction : null,
28146
28147     /**
28148      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28149      * or setValues() data instead of when the form was first created.
28150      */
28151     trackResetOnLoad : false,
28152     
28153     
28154     /**
28155      * childForms - used for multi-tab forms
28156      * @type {Array}
28157      */
28158     childForms : false,
28159     
28160     /**
28161      * allItems - full list of fields.
28162      * @type {Array}
28163      */
28164     allItems : false,
28165     
28166     /**
28167      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28168      * element by passing it or its id or mask the form itself by passing in true.
28169      * @type Mixed
28170      */
28171     waitMsgTarget : false,
28172
28173     // private
28174     initEl : function(el){
28175         this.el = Roo.get(el);
28176         this.id = this.el.id || Roo.id();
28177         this.el.on('submit', this.onSubmit, this);
28178         this.el.addClass('x-form');
28179     },
28180
28181     // private
28182     onSubmit : function(e){
28183         e.stopEvent();
28184     },
28185
28186     /**
28187      * Returns true if client-side validation on the form is successful.
28188      * @return Boolean
28189      */
28190     isValid : function(){
28191         var valid = true;
28192         this.items.each(function(f){
28193            if(!f.validate()){
28194                valid = false;
28195            }
28196         });
28197         return valid;
28198     },
28199
28200     /**
28201      * Returns true if any fields in this form have changed since their original load.
28202      * @return Boolean
28203      */
28204     isDirty : function(){
28205         var dirty = false;
28206         this.items.each(function(f){
28207            if(f.isDirty()){
28208                dirty = true;
28209                return false;
28210            }
28211         });
28212         return dirty;
28213     },
28214
28215     /**
28216      * Performs a predefined action (submit or load) or custom actions you define on this form.
28217      * @param {String} actionName The name of the action type
28218      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28219      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28220      * accept other config options):
28221      * <pre>
28222 Property          Type             Description
28223 ----------------  ---------------  ----------------------------------------------------------------------------------
28224 url               String           The url for the action (defaults to the form's url)
28225 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28226 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28227 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28228                                    validate the form on the client (defaults to false)
28229      * </pre>
28230      * @return {BasicForm} this
28231      */
28232     doAction : function(action, options){
28233         if(typeof action == 'string'){
28234             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28235         }
28236         if(this.fireEvent('beforeaction', this, action) !== false){
28237             this.beforeAction(action);
28238             action.run.defer(100, action);
28239         }
28240         return this;
28241     },
28242
28243     /**
28244      * Shortcut to do a submit action.
28245      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28246      * @return {BasicForm} this
28247      */
28248     submit : function(options){
28249         this.doAction('submit', options);
28250         return this;
28251     },
28252
28253     /**
28254      * Shortcut to do a load action.
28255      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28256      * @return {BasicForm} this
28257      */
28258     load : function(options){
28259         this.doAction('load', options);
28260         return this;
28261     },
28262
28263     /**
28264      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28265      * @param {Record} record The record to edit
28266      * @return {BasicForm} this
28267      */
28268     updateRecord : function(record){
28269         record.beginEdit();
28270         var fs = record.fields;
28271         fs.each(function(f){
28272             var field = this.findField(f.name);
28273             if(field){
28274                 record.set(f.name, field.getValue());
28275             }
28276         }, this);
28277         record.endEdit();
28278         return this;
28279     },
28280
28281     /**
28282      * Loads an Roo.data.Record into this form.
28283      * @param {Record} record The record to load
28284      * @return {BasicForm} this
28285      */
28286     loadRecord : function(record){
28287         this.setValues(record.data);
28288         return this;
28289     },
28290
28291     // private
28292     beforeAction : function(action){
28293         var o = action.options;
28294         
28295        
28296         if(this.waitMsgTarget === true){
28297             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28298         }else if(this.waitMsgTarget){
28299             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28300             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28301         }else {
28302             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28303         }
28304          
28305     },
28306
28307     // private
28308     afterAction : function(action, success){
28309         this.activeAction = null;
28310         var o = action.options;
28311         
28312         if(this.waitMsgTarget === true){
28313             this.el.unmask();
28314         }else if(this.waitMsgTarget){
28315             this.waitMsgTarget.unmask();
28316         }else{
28317             Roo.MessageBox.updateProgress(1);
28318             Roo.MessageBox.hide();
28319         }
28320          
28321         if(success){
28322             if(o.reset){
28323                 this.reset();
28324             }
28325             Roo.callback(o.success, o.scope, [this, action]);
28326             this.fireEvent('actioncomplete', this, action);
28327             
28328         }else{
28329             
28330             // failure condition..
28331             // we have a scenario where updates need confirming.
28332             // eg. if a locking scenario exists..
28333             // we look for { errors : { needs_confirm : true }} in the response.
28334             if (
28335                 (typeof(action.result) != 'undefined')  &&
28336                 (typeof(action.result.errors) != 'undefined')  &&
28337                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28338            ){
28339                 var _t = this;
28340                 Roo.MessageBox.confirm(
28341                     "Change requires confirmation",
28342                     action.result.errorMsg,
28343                     function(r) {
28344                         if (r != 'yes') {
28345                             return;
28346                         }
28347                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28348                     }
28349                     
28350                 );
28351                 
28352                 
28353                 
28354                 return;
28355             }
28356             
28357             Roo.callback(o.failure, o.scope, [this, action]);
28358             // show an error message if no failed handler is set..
28359             if (!this.hasListener('actionfailed')) {
28360                 Roo.MessageBox.alert("Error",
28361                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28362                         action.result.errorMsg :
28363                         "Saving Failed, please check your entries or try again"
28364                 );
28365             }
28366             
28367             this.fireEvent('actionfailed', this, action);
28368         }
28369         
28370     },
28371
28372     /**
28373      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28374      * @param {String} id The value to search for
28375      * @return Field
28376      */
28377     findField : function(id){
28378         var field = this.items.get(id);
28379         if(!field){
28380             this.items.each(function(f){
28381                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28382                     field = f;
28383                     return false;
28384                 }
28385             });
28386         }
28387         return field || null;
28388     },
28389
28390     /**
28391      * Add a secondary form to this one, 
28392      * Used to provide tabbed forms. One form is primary, with hidden values 
28393      * which mirror the elements from the other forms.
28394      * 
28395      * @param {Roo.form.Form} form to add.
28396      * 
28397      */
28398     addForm : function(form)
28399     {
28400        
28401         if (this.childForms.indexOf(form) > -1) {
28402             // already added..
28403             return;
28404         }
28405         this.childForms.push(form);
28406         var n = '';
28407         Roo.each(form.allItems, function (fe) {
28408             
28409             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28410             if (this.findField(n)) { // already added..
28411                 return;
28412             }
28413             var add = new Roo.form.Hidden({
28414                 name : n
28415             });
28416             add.render(this.el);
28417             
28418             this.add( add );
28419         }, this);
28420         
28421     },
28422     /**
28423      * Mark fields in this form invalid in bulk.
28424      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28425      * @return {BasicForm} this
28426      */
28427     markInvalid : function(errors){
28428         if(errors instanceof Array){
28429             for(var i = 0, len = errors.length; i < len; i++){
28430                 var fieldError = errors[i];
28431                 var f = this.findField(fieldError.id);
28432                 if(f){
28433                     f.markInvalid(fieldError.msg);
28434                 }
28435             }
28436         }else{
28437             var field, id;
28438             for(id in errors){
28439                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28440                     field.markInvalid(errors[id]);
28441                 }
28442             }
28443         }
28444         Roo.each(this.childForms || [], function (f) {
28445             f.markInvalid(errors);
28446         });
28447         
28448         return this;
28449     },
28450
28451     /**
28452      * Set values for fields in this form in bulk.
28453      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28454      * @return {BasicForm} this
28455      */
28456     setValues : function(values){
28457         if(values instanceof Array){ // array of objects
28458             for(var i = 0, len = values.length; i < len; i++){
28459                 var v = values[i];
28460                 var f = this.findField(v.id);
28461                 if(f){
28462                     f.setValue(v.value);
28463                     if(this.trackResetOnLoad){
28464                         f.originalValue = f.getValue();
28465                     }
28466                 }
28467             }
28468         }else{ // object hash
28469             var field, id;
28470             for(id in values){
28471                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28472                     
28473                     if (field.setFromData && 
28474                         field.valueField && 
28475                         field.displayField &&
28476                         // combos' with local stores can 
28477                         // be queried via setValue()
28478                         // to set their value..
28479                         (field.store && !field.store.isLocal)
28480                         ) {
28481                         // it's a combo
28482                         var sd = { };
28483                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28484                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28485                         field.setFromData(sd);
28486                         
28487                     } else {
28488                         field.setValue(values[id]);
28489                     }
28490                     
28491                     
28492                     if(this.trackResetOnLoad){
28493                         field.originalValue = field.getValue();
28494                     }
28495                 }
28496             }
28497         }
28498          
28499         Roo.each(this.childForms || [], function (f) {
28500             f.setValues(values);
28501         });
28502                 
28503         return this;
28504     },
28505
28506     /**
28507      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28508      * they are returned as an array.
28509      * @param {Boolean} asString
28510      * @return {Object}
28511      */
28512     getValues : function(asString){
28513         if (this.childForms) {
28514             // copy values from the child forms
28515             Roo.each(this.childForms, function (f) {
28516                 this.setValues(f.getValues());
28517             }, this);
28518         }
28519         
28520         
28521         
28522         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28523         if(asString === true){
28524             return fs;
28525         }
28526         return Roo.urlDecode(fs);
28527     },
28528     
28529     /**
28530      * Returns the fields in this form as an object with key/value pairs. 
28531      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28532      * @return {Object}
28533      */
28534     getFieldValues : function(with_hidden)
28535     {
28536         if (this.childForms) {
28537             // copy values from the child forms
28538             // should this call getFieldValues - probably not as we do not currently copy
28539             // hidden fields when we generate..
28540             Roo.each(this.childForms, function (f) {
28541                 this.setValues(f.getValues());
28542             }, this);
28543         }
28544         
28545         var ret = {};
28546         this.items.each(function(f){
28547             if (!f.getName()) {
28548                 return;
28549             }
28550             var v = f.getValue();
28551             if (f.inputType =='radio') {
28552                 if (typeof(ret[f.getName()]) == 'undefined') {
28553                     ret[f.getName()] = ''; // empty..
28554                 }
28555                 
28556                 if (!f.el.dom.checked) {
28557                     return;
28558                     
28559                 }
28560                 v = f.el.dom.value;
28561                 
28562             }
28563             
28564             // not sure if this supported any more..
28565             if ((typeof(v) == 'object') && f.getRawValue) {
28566                 v = f.getRawValue() ; // dates..
28567             }
28568             // combo boxes where name != hiddenName...
28569             if (f.name != f.getName()) {
28570                 ret[f.name] = f.getRawValue();
28571             }
28572             ret[f.getName()] = v;
28573         });
28574         
28575         return ret;
28576     },
28577
28578     /**
28579      * Clears all invalid messages in this form.
28580      * @return {BasicForm} this
28581      */
28582     clearInvalid : function(){
28583         this.items.each(function(f){
28584            f.clearInvalid();
28585         });
28586         
28587         Roo.each(this.childForms || [], function (f) {
28588             f.clearInvalid();
28589         });
28590         
28591         
28592         return this;
28593     },
28594
28595     /**
28596      * Resets this form.
28597      * @return {BasicForm} this
28598      */
28599     reset : function(){
28600         this.items.each(function(f){
28601             f.reset();
28602         });
28603         
28604         Roo.each(this.childForms || [], function (f) {
28605             f.reset();
28606         });
28607        
28608         
28609         return this;
28610     },
28611
28612     /**
28613      * Add Roo.form components to this form.
28614      * @param {Field} field1
28615      * @param {Field} field2 (optional)
28616      * @param {Field} etc (optional)
28617      * @return {BasicForm} this
28618      */
28619     add : function(){
28620         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28621         return this;
28622     },
28623
28624
28625     /**
28626      * Removes a field from the items collection (does NOT remove its markup).
28627      * @param {Field} field
28628      * @return {BasicForm} this
28629      */
28630     remove : function(field){
28631         this.items.remove(field);
28632         return this;
28633     },
28634
28635     /**
28636      * Looks at the fields in this form, checks them for an id attribute,
28637      * and calls applyTo on the existing dom element with that id.
28638      * @return {BasicForm} this
28639      */
28640     render : function(){
28641         this.items.each(function(f){
28642             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28643                 f.applyTo(f.id);
28644             }
28645         });
28646         return this;
28647     },
28648
28649     /**
28650      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28651      * @param {Object} values
28652      * @return {BasicForm} this
28653      */
28654     applyToFields : function(o){
28655         this.items.each(function(f){
28656            Roo.apply(f, o);
28657         });
28658         return this;
28659     },
28660
28661     /**
28662      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28663      * @param {Object} values
28664      * @return {BasicForm} this
28665      */
28666     applyIfToFields : function(o){
28667         this.items.each(function(f){
28668            Roo.applyIf(f, o);
28669         });
28670         return this;
28671     }
28672 });
28673
28674 // back compat
28675 Roo.BasicForm = Roo.form.BasicForm;/*
28676  * Based on:
28677  * Ext JS Library 1.1.1
28678  * Copyright(c) 2006-2007, Ext JS, LLC.
28679  *
28680  * Originally Released Under LGPL - original licence link has changed is not relivant.
28681  *
28682  * Fork - LGPL
28683  * <script type="text/javascript">
28684  */
28685
28686 /**
28687  * @class Roo.form.Form
28688  * @extends Roo.form.BasicForm
28689  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28690  * @constructor
28691  * @param {Object} config Configuration options
28692  */
28693 Roo.form.Form = function(config){
28694     var xitems =  [];
28695     if (config.items) {
28696         xitems = config.items;
28697         delete config.items;
28698     }
28699    
28700     
28701     Roo.form.Form.superclass.constructor.call(this, null, config);
28702     this.url = this.url || this.action;
28703     if(!this.root){
28704         this.root = new Roo.form.Layout(Roo.applyIf({
28705             id: Roo.id()
28706         }, config));
28707     }
28708     this.active = this.root;
28709     /**
28710      * Array of all the buttons that have been added to this form via {@link addButton}
28711      * @type Array
28712      */
28713     this.buttons = [];
28714     this.allItems = [];
28715     this.addEvents({
28716         /**
28717          * @event clientvalidation
28718          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28719          * @param {Form} this
28720          * @param {Boolean} valid true if the form has passed client-side validation
28721          */
28722         clientvalidation: true,
28723         /**
28724          * @event rendered
28725          * Fires when the form is rendered
28726          * @param {Roo.form.Form} form
28727          */
28728         rendered : true
28729     });
28730     
28731     if (this.progressUrl) {
28732             // push a hidden field onto the list of fields..
28733             this.addxtype( {
28734                     xns: Roo.form, 
28735                     xtype : 'Hidden', 
28736                     name : 'UPLOAD_IDENTIFIER' 
28737             });
28738         }
28739         
28740     
28741     Roo.each(xitems, this.addxtype, this);
28742     
28743     
28744     
28745 };
28746
28747 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28748     /**
28749      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28750      */
28751     /**
28752      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28753      */
28754     /**
28755      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28756      */
28757     buttonAlign:'center',
28758
28759     /**
28760      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28761      */
28762     minButtonWidth:75,
28763
28764     /**
28765      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28766      * This property cascades to child containers if not set.
28767      */
28768     labelAlign:'left',
28769
28770     /**
28771      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28772      * fires a looping event with that state. This is required to bind buttons to the valid
28773      * state using the config value formBind:true on the button.
28774      */
28775     monitorValid : false,
28776
28777     /**
28778      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28779      */
28780     monitorPoll : 200,
28781     
28782     /**
28783      * @cfg {String} progressUrl - Url to return progress data 
28784      */
28785     
28786     progressUrl : false,
28787   
28788     /**
28789      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28790      * fields are added and the column is closed. If no fields are passed the column remains open
28791      * until end() is called.
28792      * @param {Object} config The config to pass to the column
28793      * @param {Field} field1 (optional)
28794      * @param {Field} field2 (optional)
28795      * @param {Field} etc (optional)
28796      * @return Column The column container object
28797      */
28798     column : function(c){
28799         var col = new Roo.form.Column(c);
28800         this.start(col);
28801         if(arguments.length > 1){ // duplicate code required because of Opera
28802             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28803             this.end();
28804         }
28805         return col;
28806     },
28807
28808     /**
28809      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28810      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28811      * until end() is called.
28812      * @param {Object} config The config to pass to the fieldset
28813      * @param {Field} field1 (optional)
28814      * @param {Field} field2 (optional)
28815      * @param {Field} etc (optional)
28816      * @return FieldSet The fieldset container object
28817      */
28818     fieldset : function(c){
28819         var fs = new Roo.form.FieldSet(c);
28820         this.start(fs);
28821         if(arguments.length > 1){ // duplicate code required because of Opera
28822             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28823             this.end();
28824         }
28825         return fs;
28826     },
28827
28828     /**
28829      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28830      * fields are added and the container is closed. If no fields are passed the container remains open
28831      * until end() is called.
28832      * @param {Object} config The config to pass to the Layout
28833      * @param {Field} field1 (optional)
28834      * @param {Field} field2 (optional)
28835      * @param {Field} etc (optional)
28836      * @return Layout The container object
28837      */
28838     container : function(c){
28839         var l = new Roo.form.Layout(c);
28840         this.start(l);
28841         if(arguments.length > 1){ // duplicate code required because of Opera
28842             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28843             this.end();
28844         }
28845         return l;
28846     },
28847
28848     /**
28849      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28850      * @param {Object} container A Roo.form.Layout or subclass of Layout
28851      * @return {Form} this
28852      */
28853     start : function(c){
28854         // cascade label info
28855         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28856         this.active.stack.push(c);
28857         c.ownerCt = this.active;
28858         this.active = c;
28859         return this;
28860     },
28861
28862     /**
28863      * Closes the current open container
28864      * @return {Form} this
28865      */
28866     end : function(){
28867         if(this.active == this.root){
28868             return this;
28869         }
28870         this.active = this.active.ownerCt;
28871         return this;
28872     },
28873
28874     /**
28875      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28876      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28877      * as the label of the field.
28878      * @param {Field} field1
28879      * @param {Field} field2 (optional)
28880      * @param {Field} etc. (optional)
28881      * @return {Form} this
28882      */
28883     add : function(){
28884         this.active.stack.push.apply(this.active.stack, arguments);
28885         this.allItems.push.apply(this.allItems,arguments);
28886         var r = [];
28887         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28888             if(a[i].isFormField){
28889                 r.push(a[i]);
28890             }
28891         }
28892         if(r.length > 0){
28893             Roo.form.Form.superclass.add.apply(this, r);
28894         }
28895         return this;
28896     },
28897     
28898
28899     
28900     
28901     
28902      /**
28903      * Find any element that has been added to a form, using it's ID or name
28904      * This can include framesets, columns etc. along with regular fields..
28905      * @param {String} id - id or name to find.
28906      
28907      * @return {Element} e - or false if nothing found.
28908      */
28909     findbyId : function(id)
28910     {
28911         var ret = false;
28912         if (!id) {
28913             return ret;
28914         }
28915         Roo.each(this.allItems, function(f){
28916             if (f.id == id || f.name == id ){
28917                 ret = f;
28918                 return false;
28919             }
28920         });
28921         return ret;
28922     },
28923
28924     
28925     
28926     /**
28927      * Render this form into the passed container. This should only be called once!
28928      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28929      * @return {Form} this
28930      */
28931     render : function(ct)
28932     {
28933         
28934         
28935         
28936         ct = Roo.get(ct);
28937         var o = this.autoCreate || {
28938             tag: 'form',
28939             method : this.method || 'POST',
28940             id : this.id || Roo.id()
28941         };
28942         this.initEl(ct.createChild(o));
28943
28944         this.root.render(this.el);
28945         
28946        
28947              
28948         this.items.each(function(f){
28949             f.render('x-form-el-'+f.id);
28950         });
28951
28952         if(this.buttons.length > 0){
28953             // tables are required to maintain order and for correct IE layout
28954             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28955                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28956                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28957             }}, null, true);
28958             var tr = tb.getElementsByTagName('tr')[0];
28959             for(var i = 0, len = this.buttons.length; i < len; i++) {
28960                 var b = this.buttons[i];
28961                 var td = document.createElement('td');
28962                 td.className = 'x-form-btn-td';
28963                 b.render(tr.appendChild(td));
28964             }
28965         }
28966         if(this.monitorValid){ // initialize after render
28967             this.startMonitoring();
28968         }
28969         this.fireEvent('rendered', this);
28970         return this;
28971     },
28972
28973     /**
28974      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28975      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28976      * object or a valid Roo.DomHelper element config
28977      * @param {Function} handler The function called when the button is clicked
28978      * @param {Object} scope (optional) The scope of the handler function
28979      * @return {Roo.Button}
28980      */
28981     addButton : function(config, handler, scope){
28982         var bc = {
28983             handler: handler,
28984             scope: scope,
28985             minWidth: this.minButtonWidth,
28986             hideParent:true
28987         };
28988         if(typeof config == "string"){
28989             bc.text = config;
28990         }else{
28991             Roo.apply(bc, config);
28992         }
28993         var btn = new Roo.Button(null, bc);
28994         this.buttons.push(btn);
28995         return btn;
28996     },
28997
28998      /**
28999      * Adds a series of form elements (using the xtype property as the factory method.
29000      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29001      * @param {Object} config 
29002      */
29003     
29004     addxtype : function()
29005     {
29006         var ar = Array.prototype.slice.call(arguments, 0);
29007         var ret = false;
29008         for(var i = 0; i < ar.length; i++) {
29009             if (!ar[i]) {
29010                 continue; // skip -- if this happends something invalid got sent, we 
29011                 // should ignore it, as basically that interface element will not show up
29012                 // and that should be pretty obvious!!
29013             }
29014             
29015             if (Roo.form[ar[i].xtype]) {
29016                 ar[i].form = this;
29017                 var fe = Roo.factory(ar[i], Roo.form);
29018                 if (!ret) {
29019                     ret = fe;
29020                 }
29021                 fe.form = this;
29022                 if (fe.store) {
29023                     fe.store.form = this;
29024                 }
29025                 if (fe.isLayout) {  
29026                          
29027                     this.start(fe);
29028                     this.allItems.push(fe);
29029                     if (fe.items && fe.addxtype) {
29030                         fe.addxtype.apply(fe, fe.items);
29031                         delete fe.items;
29032                     }
29033                      this.end();
29034                     continue;
29035                 }
29036                 
29037                 
29038                  
29039                 this.add(fe);
29040               //  console.log('adding ' + ar[i].xtype);
29041             }
29042             if (ar[i].xtype == 'Button') {  
29043                 //console.log('adding button');
29044                 //console.log(ar[i]);
29045                 this.addButton(ar[i]);
29046                 this.allItems.push(fe);
29047                 continue;
29048             }
29049             
29050             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29051                 alert('end is not supported on xtype any more, use items');
29052             //    this.end();
29053             //    //console.log('adding end');
29054             }
29055             
29056         }
29057         return ret;
29058     },
29059     
29060     /**
29061      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29062      * option "monitorValid"
29063      */
29064     startMonitoring : function(){
29065         if(!this.bound){
29066             this.bound = true;
29067             Roo.TaskMgr.start({
29068                 run : this.bindHandler,
29069                 interval : this.monitorPoll || 200,
29070                 scope: this
29071             });
29072         }
29073     },
29074
29075     /**
29076      * Stops monitoring of the valid state of this form
29077      */
29078     stopMonitoring : function(){
29079         this.bound = false;
29080     },
29081
29082     // private
29083     bindHandler : function(){
29084         if(!this.bound){
29085             return false; // stops binding
29086         }
29087         var valid = true;
29088         this.items.each(function(f){
29089             if(!f.isValid(true)){
29090                 valid = false;
29091                 return false;
29092             }
29093         });
29094         for(var i = 0, len = this.buttons.length; i < len; i++){
29095             var btn = this.buttons[i];
29096             if(btn.formBind === true && btn.disabled === valid){
29097                 btn.setDisabled(!valid);
29098             }
29099         }
29100         this.fireEvent('clientvalidation', this, valid);
29101     }
29102     
29103     
29104     
29105     
29106     
29107     
29108     
29109     
29110 });
29111
29112
29113 // back compat
29114 Roo.Form = Roo.form.Form;
29115 /*
29116  * Based on:
29117  * Ext JS Library 1.1.1
29118  * Copyright(c) 2006-2007, Ext JS, LLC.
29119  *
29120  * Originally Released Under LGPL - original licence link has changed is not relivant.
29121  *
29122  * Fork - LGPL
29123  * <script type="text/javascript">
29124  */
29125  
29126  /**
29127  * @class Roo.form.Action
29128  * Internal Class used to handle form actions
29129  * @constructor
29130  * @param {Roo.form.BasicForm} el The form element or its id
29131  * @param {Object} config Configuration options
29132  */
29133  
29134  
29135 // define the action interface
29136 Roo.form.Action = function(form, options){
29137     this.form = form;
29138     this.options = options || {};
29139 };
29140 /**
29141  * Client Validation Failed
29142  * @const 
29143  */
29144 Roo.form.Action.CLIENT_INVALID = 'client';
29145 /**
29146  * Server Validation Failed
29147  * @const 
29148  */
29149  Roo.form.Action.SERVER_INVALID = 'server';
29150  /**
29151  * Connect to Server Failed
29152  * @const 
29153  */
29154 Roo.form.Action.CONNECT_FAILURE = 'connect';
29155 /**
29156  * Reading Data from Server Failed
29157  * @const 
29158  */
29159 Roo.form.Action.LOAD_FAILURE = 'load';
29160
29161 Roo.form.Action.prototype = {
29162     type : 'default',
29163     failureType : undefined,
29164     response : undefined,
29165     result : undefined,
29166
29167     // interface method
29168     run : function(options){
29169
29170     },
29171
29172     // interface method
29173     success : function(response){
29174
29175     },
29176
29177     // interface method
29178     handleResponse : function(response){
29179
29180     },
29181
29182     // default connection failure
29183     failure : function(response){
29184         
29185         this.response = response;
29186         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29187         this.form.afterAction(this, false);
29188     },
29189
29190     processResponse : function(response){
29191         this.response = response;
29192         if(!response.responseText){
29193             return true;
29194         }
29195         this.result = this.handleResponse(response);
29196         return this.result;
29197     },
29198
29199     // utility functions used internally
29200     getUrl : function(appendParams){
29201         var url = this.options.url || this.form.url || this.form.el.dom.action;
29202         if(appendParams){
29203             var p = this.getParams();
29204             if(p){
29205                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29206             }
29207         }
29208         return url;
29209     },
29210
29211     getMethod : function(){
29212         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29213     },
29214
29215     getParams : function(){
29216         var bp = this.form.baseParams;
29217         var p = this.options.params;
29218         if(p){
29219             if(typeof p == "object"){
29220                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29221             }else if(typeof p == 'string' && bp){
29222                 p += '&' + Roo.urlEncode(bp);
29223             }
29224         }else if(bp){
29225             p = Roo.urlEncode(bp);
29226         }
29227         return p;
29228     },
29229
29230     createCallback : function(){
29231         return {
29232             success: this.success,
29233             failure: this.failure,
29234             scope: this,
29235             timeout: (this.form.timeout*1000),
29236             upload: this.form.fileUpload ? this.success : undefined
29237         };
29238     }
29239 };
29240
29241 Roo.form.Action.Submit = function(form, options){
29242     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29243 };
29244
29245 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29246     type : 'submit',
29247
29248     haveProgress : false,
29249     uploadComplete : false,
29250     
29251     // uploadProgress indicator.
29252     uploadProgress : function()
29253     {
29254         if (!this.form.progressUrl) {
29255             return;
29256         }
29257         
29258         if (!this.haveProgress) {
29259             Roo.MessageBox.progress("Uploading", "Uploading");
29260         }
29261         if (this.uploadComplete) {
29262            Roo.MessageBox.hide();
29263            return;
29264         }
29265         
29266         this.haveProgress = true;
29267    
29268         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29269         
29270         var c = new Roo.data.Connection();
29271         c.request({
29272             url : this.form.progressUrl,
29273             params: {
29274                 id : uid
29275             },
29276             method: 'GET',
29277             success : function(req){
29278                //console.log(data);
29279                 var rdata = false;
29280                 var edata;
29281                 try  {
29282                    rdata = Roo.decode(req.responseText)
29283                 } catch (e) {
29284                     Roo.log("Invalid data from server..");
29285                     Roo.log(edata);
29286                     return;
29287                 }
29288                 if (!rdata || !rdata.success) {
29289                     Roo.log(rdata);
29290                     Roo.MessageBox.alert(Roo.encode(rdata));
29291                     return;
29292                 }
29293                 var data = rdata.data;
29294                 
29295                 if (this.uploadComplete) {
29296                    Roo.MessageBox.hide();
29297                    return;
29298                 }
29299                    
29300                 if (data){
29301                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29302                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29303                     );
29304                 }
29305                 this.uploadProgress.defer(2000,this);
29306             },
29307        
29308             failure: function(data) {
29309                 Roo.log('progress url failed ');
29310                 Roo.log(data);
29311             },
29312             scope : this
29313         });
29314            
29315     },
29316     
29317     
29318     run : function()
29319     {
29320         // run get Values on the form, so it syncs any secondary forms.
29321         this.form.getValues();
29322         
29323         var o = this.options;
29324         var method = this.getMethod();
29325         var isPost = method == 'POST';
29326         if(o.clientValidation === false || this.form.isValid()){
29327             
29328             if (this.form.progressUrl) {
29329                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29330                     (new Date() * 1) + '' + Math.random());
29331                     
29332             } 
29333             
29334             
29335             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29336                 form:this.form.el.dom,
29337                 url:this.getUrl(!isPost),
29338                 method: method,
29339                 params:isPost ? this.getParams() : null,
29340                 isUpload: this.form.fileUpload
29341             }));
29342             
29343             this.uploadProgress();
29344
29345         }else if (o.clientValidation !== false){ // client validation failed
29346             this.failureType = Roo.form.Action.CLIENT_INVALID;
29347             this.form.afterAction(this, false);
29348         }
29349     },
29350
29351     success : function(response)
29352     {
29353         this.uploadComplete= true;
29354         if (this.haveProgress) {
29355             Roo.MessageBox.hide();
29356         }
29357         
29358         
29359         var result = this.processResponse(response);
29360         if(result === true || result.success){
29361             this.form.afterAction(this, true);
29362             return;
29363         }
29364         if(result.errors){
29365             this.form.markInvalid(result.errors);
29366             this.failureType = Roo.form.Action.SERVER_INVALID;
29367         }
29368         this.form.afterAction(this, false);
29369     },
29370     failure : function(response)
29371     {
29372         this.uploadComplete= true;
29373         if (this.haveProgress) {
29374             Roo.MessageBox.hide();
29375         }
29376         
29377         this.response = response;
29378         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29379         this.form.afterAction(this, false);
29380     },
29381     
29382     handleResponse : function(response){
29383         if(this.form.errorReader){
29384             var rs = this.form.errorReader.read(response);
29385             var errors = [];
29386             if(rs.records){
29387                 for(var i = 0, len = rs.records.length; i < len; i++) {
29388                     var r = rs.records[i];
29389                     errors[i] = r.data;
29390                 }
29391             }
29392             if(errors.length < 1){
29393                 errors = null;
29394             }
29395             return {
29396                 success : rs.success,
29397                 errors : errors
29398             };
29399         }
29400         var ret = false;
29401         try {
29402             ret = Roo.decode(response.responseText);
29403         } catch (e) {
29404             ret = {
29405                 success: false,
29406                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29407                 errors : []
29408             };
29409         }
29410         return ret;
29411         
29412     }
29413 });
29414
29415
29416 Roo.form.Action.Load = function(form, options){
29417     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29418     this.reader = this.form.reader;
29419 };
29420
29421 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29422     type : 'load',
29423
29424     run : function(){
29425         
29426         Roo.Ajax.request(Roo.apply(
29427                 this.createCallback(), {
29428                     method:this.getMethod(),
29429                     url:this.getUrl(false),
29430                     params:this.getParams()
29431         }));
29432     },
29433
29434     success : function(response){
29435         
29436         var result = this.processResponse(response);
29437         if(result === true || !result.success || !result.data){
29438             this.failureType = Roo.form.Action.LOAD_FAILURE;
29439             this.form.afterAction(this, false);
29440             return;
29441         }
29442         this.form.clearInvalid();
29443         this.form.setValues(result.data);
29444         this.form.afterAction(this, true);
29445     },
29446
29447     handleResponse : function(response){
29448         if(this.form.reader){
29449             var rs = this.form.reader.read(response);
29450             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29451             return {
29452                 success : rs.success,
29453                 data : data
29454             };
29455         }
29456         return Roo.decode(response.responseText);
29457     }
29458 });
29459
29460 Roo.form.Action.ACTION_TYPES = {
29461     'load' : Roo.form.Action.Load,
29462     'submit' : Roo.form.Action.Submit
29463 };/*
29464  * Based on:
29465  * Ext JS Library 1.1.1
29466  * Copyright(c) 2006-2007, Ext JS, LLC.
29467  *
29468  * Originally Released Under LGPL - original licence link has changed is not relivant.
29469  *
29470  * Fork - LGPL
29471  * <script type="text/javascript">
29472  */
29473  
29474 /**
29475  * @class Roo.form.Layout
29476  * @extends Roo.Component
29477  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29478  * @constructor
29479  * @param {Object} config Configuration options
29480  */
29481 Roo.form.Layout = function(config){
29482     var xitems = [];
29483     if (config.items) {
29484         xitems = config.items;
29485         delete config.items;
29486     }
29487     Roo.form.Layout.superclass.constructor.call(this, config);
29488     this.stack = [];
29489     Roo.each(xitems, this.addxtype, this);
29490      
29491 };
29492
29493 Roo.extend(Roo.form.Layout, Roo.Component, {
29494     /**
29495      * @cfg {String/Object} autoCreate
29496      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29497      */
29498     /**
29499      * @cfg {String/Object/Function} style
29500      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29501      * a function which returns such a specification.
29502      */
29503     /**
29504      * @cfg {String} labelAlign
29505      * Valid values are "left," "top" and "right" (defaults to "left")
29506      */
29507     /**
29508      * @cfg {Number} labelWidth
29509      * Fixed width in pixels of all field labels (defaults to undefined)
29510      */
29511     /**
29512      * @cfg {Boolean} clear
29513      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29514      */
29515     clear : true,
29516     /**
29517      * @cfg {String} labelSeparator
29518      * The separator to use after field labels (defaults to ':')
29519      */
29520     labelSeparator : ':',
29521     /**
29522      * @cfg {Boolean} hideLabels
29523      * True to suppress the display of field labels in this layout (defaults to false)
29524      */
29525     hideLabels : false,
29526
29527     // private
29528     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29529     
29530     isLayout : true,
29531     
29532     // private
29533     onRender : function(ct, position){
29534         if(this.el){ // from markup
29535             this.el = Roo.get(this.el);
29536         }else {  // generate
29537             var cfg = this.getAutoCreate();
29538             this.el = ct.createChild(cfg, position);
29539         }
29540         if(this.style){
29541             this.el.applyStyles(this.style);
29542         }
29543         if(this.labelAlign){
29544             this.el.addClass('x-form-label-'+this.labelAlign);
29545         }
29546         if(this.hideLabels){
29547             this.labelStyle = "display:none";
29548             this.elementStyle = "padding-left:0;";
29549         }else{
29550             if(typeof this.labelWidth == 'number'){
29551                 this.labelStyle = "width:"+this.labelWidth+"px;";
29552                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29553             }
29554             if(this.labelAlign == 'top'){
29555                 this.labelStyle = "width:auto;";
29556                 this.elementStyle = "padding-left:0;";
29557             }
29558         }
29559         var stack = this.stack;
29560         var slen = stack.length;
29561         if(slen > 0){
29562             if(!this.fieldTpl){
29563                 var t = new Roo.Template(
29564                     '<div class="x-form-item {5}">',
29565                         '<label for="{0}" style="{2}">{1}{4}</label>',
29566                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29567                         '</div>',
29568                     '</div><div class="x-form-clear-left"></div>'
29569                 );
29570                 t.disableFormats = true;
29571                 t.compile();
29572                 Roo.form.Layout.prototype.fieldTpl = t;
29573             }
29574             for(var i = 0; i < slen; i++) {
29575                 if(stack[i].isFormField){
29576                     this.renderField(stack[i]);
29577                 }else{
29578                     this.renderComponent(stack[i]);
29579                 }
29580             }
29581         }
29582         if(this.clear){
29583             this.el.createChild({cls:'x-form-clear'});
29584         }
29585     },
29586
29587     // private
29588     renderField : function(f){
29589         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29590                f.id, //0
29591                f.fieldLabel, //1
29592                f.labelStyle||this.labelStyle||'', //2
29593                this.elementStyle||'', //3
29594                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29595                f.itemCls||this.itemCls||''  //5
29596        ], true).getPrevSibling());
29597     },
29598
29599     // private
29600     renderComponent : function(c){
29601         c.render(c.isLayout ? this.el : this.el.createChild());    
29602     },
29603     /**
29604      * Adds a object form elements (using the xtype property as the factory method.)
29605      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29606      * @param {Object} config 
29607      */
29608     addxtype : function(o)
29609     {
29610         // create the lement.
29611         o.form = this.form;
29612         var fe = Roo.factory(o, Roo.form);
29613         this.form.allItems.push(fe);
29614         this.stack.push(fe);
29615         
29616         if (fe.isFormField) {
29617             this.form.items.add(fe);
29618         }
29619          
29620         return fe;
29621     }
29622 });
29623
29624 /**
29625  * @class Roo.form.Column
29626  * @extends Roo.form.Layout
29627  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29628  * @constructor
29629  * @param {Object} config Configuration options
29630  */
29631 Roo.form.Column = function(config){
29632     Roo.form.Column.superclass.constructor.call(this, config);
29633 };
29634
29635 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29636     /**
29637      * @cfg {Number/String} width
29638      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29639      */
29640     /**
29641      * @cfg {String/Object} autoCreate
29642      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29643      */
29644
29645     // private
29646     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29647
29648     // private
29649     onRender : function(ct, position){
29650         Roo.form.Column.superclass.onRender.call(this, ct, position);
29651         if(this.width){
29652             this.el.setWidth(this.width);
29653         }
29654     }
29655 });
29656
29657
29658 /**
29659  * @class Roo.form.Row
29660  * @extends Roo.form.Layout
29661  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29662  * @constructor
29663  * @param {Object} config Configuration options
29664  */
29665
29666  
29667 Roo.form.Row = function(config){
29668     Roo.form.Row.superclass.constructor.call(this, config);
29669 };
29670  
29671 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29672       /**
29673      * @cfg {Number/String} width
29674      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29675      */
29676     /**
29677      * @cfg {Number/String} height
29678      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29679      */
29680     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29681     
29682     padWidth : 20,
29683     // private
29684     onRender : function(ct, position){
29685         //console.log('row render');
29686         if(!this.rowTpl){
29687             var t = new Roo.Template(
29688                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29689                     '<label for="{0}" style="{2}">{1}{4}</label>',
29690                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29691                     '</div>',
29692                 '</div>'
29693             );
29694             t.disableFormats = true;
29695             t.compile();
29696             Roo.form.Layout.prototype.rowTpl = t;
29697         }
29698         this.fieldTpl = this.rowTpl;
29699         
29700         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29701         var labelWidth = 100;
29702         
29703         if ((this.labelAlign != 'top')) {
29704             if (typeof this.labelWidth == 'number') {
29705                 labelWidth = this.labelWidth
29706             }
29707             this.padWidth =  20 + labelWidth;
29708             
29709         }
29710         
29711         Roo.form.Column.superclass.onRender.call(this, ct, position);
29712         if(this.width){
29713             this.el.setWidth(this.width);
29714         }
29715         if(this.height){
29716             this.el.setHeight(this.height);
29717         }
29718     },
29719     
29720     // private
29721     renderField : function(f){
29722         f.fieldEl = this.fieldTpl.append(this.el, [
29723                f.id, f.fieldLabel,
29724                f.labelStyle||this.labelStyle||'',
29725                this.elementStyle||'',
29726                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29727                f.itemCls||this.itemCls||'',
29728                f.width ? f.width + this.padWidth : 160 + this.padWidth
29729        ],true);
29730     }
29731 });
29732  
29733
29734 /**
29735  * @class Roo.form.FieldSet
29736  * @extends Roo.form.Layout
29737  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29738  * @constructor
29739  * @param {Object} config Configuration options
29740  */
29741 Roo.form.FieldSet = function(config){
29742     Roo.form.FieldSet.superclass.constructor.call(this, config);
29743 };
29744
29745 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29746     /**
29747      * @cfg {String} legend
29748      * The text to display as the legend for the FieldSet (defaults to '')
29749      */
29750     /**
29751      * @cfg {String/Object} autoCreate
29752      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29753      */
29754
29755     // private
29756     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29757
29758     // private
29759     onRender : function(ct, position){
29760         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29761         if(this.legend){
29762             this.setLegend(this.legend);
29763         }
29764     },
29765
29766     // private
29767     setLegend : function(text){
29768         if(this.rendered){
29769             this.el.child('legend').update(text);
29770         }
29771     }
29772 });/*
29773  * Based on:
29774  * Ext JS Library 1.1.1
29775  * Copyright(c) 2006-2007, Ext JS, LLC.
29776  *
29777  * Originally Released Under LGPL - original licence link has changed is not relivant.
29778  *
29779  * Fork - LGPL
29780  * <script type="text/javascript">
29781  */
29782 /**
29783  * @class Roo.form.VTypes
29784  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29785  * @singleton
29786  */
29787 Roo.form.VTypes = function(){
29788     // closure these in so they are only created once.
29789     var alpha = /^[a-zA-Z_]+$/;
29790     var alphanum = /^[a-zA-Z0-9_]+$/;
29791     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29792     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29793
29794     // All these messages and functions are configurable
29795     return {
29796         /**
29797          * The function used to validate email addresses
29798          * @param {String} value The email address
29799          */
29800         'email' : function(v){
29801             return email.test(v);
29802         },
29803         /**
29804          * The error text to display when the email validation function returns false
29805          * @type String
29806          */
29807         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29808         /**
29809          * The keystroke filter mask to be applied on email input
29810          * @type RegExp
29811          */
29812         'emailMask' : /[a-z0-9_\.\-@]/i,
29813
29814         /**
29815          * The function used to validate URLs
29816          * @param {String} value The URL
29817          */
29818         'url' : function(v){
29819             return url.test(v);
29820         },
29821         /**
29822          * The error text to display when the url validation function returns false
29823          * @type String
29824          */
29825         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29826         
29827         /**
29828          * The function used to validate alpha values
29829          * @param {String} value The value
29830          */
29831         'alpha' : function(v){
29832             return alpha.test(v);
29833         },
29834         /**
29835          * The error text to display when the alpha validation function returns false
29836          * @type String
29837          */
29838         'alphaText' : 'This field should only contain letters and _',
29839         /**
29840          * The keystroke filter mask to be applied on alpha input
29841          * @type RegExp
29842          */
29843         'alphaMask' : /[a-z_]/i,
29844
29845         /**
29846          * The function used to validate alphanumeric values
29847          * @param {String} value The value
29848          */
29849         'alphanum' : function(v){
29850             return alphanum.test(v);
29851         },
29852         /**
29853          * The error text to display when the alphanumeric validation function returns false
29854          * @type String
29855          */
29856         'alphanumText' : 'This field should only contain letters, numbers and _',
29857         /**
29858          * The keystroke filter mask to be applied on alphanumeric input
29859          * @type RegExp
29860          */
29861         'alphanumMask' : /[a-z0-9_]/i
29862     };
29863 }();//<script type="text/javascript">
29864
29865 /**
29866  * @class Roo.form.FCKeditor
29867  * @extends Roo.form.TextArea
29868  * Wrapper around the FCKEditor http://www.fckeditor.net
29869  * @constructor
29870  * Creates a new FCKeditor
29871  * @param {Object} config Configuration options
29872  */
29873 Roo.form.FCKeditor = function(config){
29874     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29875     this.addEvents({
29876          /**
29877          * @event editorinit
29878          * Fired when the editor is initialized - you can add extra handlers here..
29879          * @param {FCKeditor} this
29880          * @param {Object} the FCK object.
29881          */
29882         editorinit : true
29883     });
29884     
29885     
29886 };
29887 Roo.form.FCKeditor.editors = { };
29888 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29889 {
29890     //defaultAutoCreate : {
29891     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29892     //},
29893     // private
29894     /**
29895      * @cfg {Object} fck options - see fck manual for details.
29896      */
29897     fckconfig : false,
29898     
29899     /**
29900      * @cfg {Object} fck toolbar set (Basic or Default)
29901      */
29902     toolbarSet : 'Basic',
29903     /**
29904      * @cfg {Object} fck BasePath
29905      */ 
29906     basePath : '/fckeditor/',
29907     
29908     
29909     frame : false,
29910     
29911     value : '',
29912     
29913    
29914     onRender : function(ct, position)
29915     {
29916         if(!this.el){
29917             this.defaultAutoCreate = {
29918                 tag: "textarea",
29919                 style:"width:300px;height:60px;",
29920                 autocomplete: "off"
29921             };
29922         }
29923         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29924         /*
29925         if(this.grow){
29926             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29927             if(this.preventScrollbars){
29928                 this.el.setStyle("overflow", "hidden");
29929             }
29930             this.el.setHeight(this.growMin);
29931         }
29932         */
29933         //console.log('onrender' + this.getId() );
29934         Roo.form.FCKeditor.editors[this.getId()] = this;
29935          
29936
29937         this.replaceTextarea() ;
29938         
29939     },
29940     
29941     getEditor : function() {
29942         return this.fckEditor;
29943     },
29944     /**
29945      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29946      * @param {Mixed} value The value to set
29947      */
29948     
29949     
29950     setValue : function(value)
29951     {
29952         //console.log('setValue: ' + value);
29953         
29954         if(typeof(value) == 'undefined') { // not sure why this is happending...
29955             return;
29956         }
29957         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29958         
29959         //if(!this.el || !this.getEditor()) {
29960         //    this.value = value;
29961             //this.setValue.defer(100,this,[value]);    
29962         //    return;
29963         //} 
29964         
29965         if(!this.getEditor()) {
29966             return;
29967         }
29968         
29969         this.getEditor().SetData(value);
29970         
29971         //
29972
29973     },
29974
29975     /**
29976      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29977      * @return {Mixed} value The field value
29978      */
29979     getValue : function()
29980     {
29981         
29982         if (this.frame && this.frame.dom.style.display == 'none') {
29983             return Roo.form.FCKeditor.superclass.getValue.call(this);
29984         }
29985         
29986         if(!this.el || !this.getEditor()) {
29987            
29988            // this.getValue.defer(100,this); 
29989             return this.value;
29990         }
29991        
29992         
29993         var value=this.getEditor().GetData();
29994         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29995         return Roo.form.FCKeditor.superclass.getValue.call(this);
29996         
29997
29998     },
29999
30000     /**
30001      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30002      * @return {Mixed} value The field value
30003      */
30004     getRawValue : function()
30005     {
30006         if (this.frame && this.frame.dom.style.display == 'none') {
30007             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30008         }
30009         
30010         if(!this.el || !this.getEditor()) {
30011             //this.getRawValue.defer(100,this); 
30012             return this.value;
30013             return;
30014         }
30015         
30016         
30017         
30018         var value=this.getEditor().GetData();
30019         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30020         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30021          
30022     },
30023     
30024     setSize : function(w,h) {
30025         
30026         
30027         
30028         //if (this.frame && this.frame.dom.style.display == 'none') {
30029         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30030         //    return;
30031         //}
30032         //if(!this.el || !this.getEditor()) {
30033         //    this.setSize.defer(100,this, [w,h]); 
30034         //    return;
30035         //}
30036         
30037         
30038         
30039         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30040         
30041         this.frame.dom.setAttribute('width', w);
30042         this.frame.dom.setAttribute('height', h);
30043         this.frame.setSize(w,h);
30044         
30045     },
30046     
30047     toggleSourceEdit : function(value) {
30048         
30049       
30050          
30051         this.el.dom.style.display = value ? '' : 'none';
30052         this.frame.dom.style.display = value ?  'none' : '';
30053         
30054     },
30055     
30056     
30057     focus: function(tag)
30058     {
30059         if (this.frame.dom.style.display == 'none') {
30060             return Roo.form.FCKeditor.superclass.focus.call(this);
30061         }
30062         if(!this.el || !this.getEditor()) {
30063             this.focus.defer(100,this, [tag]); 
30064             return;
30065         }
30066         
30067         
30068         
30069         
30070         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30071         this.getEditor().Focus();
30072         if (tgs.length) {
30073             if (!this.getEditor().Selection.GetSelection()) {
30074                 this.focus.defer(100,this, [tag]); 
30075                 return;
30076             }
30077             
30078             
30079             var r = this.getEditor().EditorDocument.createRange();
30080             r.setStart(tgs[0],0);
30081             r.setEnd(tgs[0],0);
30082             this.getEditor().Selection.GetSelection().removeAllRanges();
30083             this.getEditor().Selection.GetSelection().addRange(r);
30084             this.getEditor().Focus();
30085         }
30086         
30087     },
30088     
30089     
30090     
30091     replaceTextarea : function()
30092     {
30093         if ( document.getElementById( this.getId() + '___Frame' ) )
30094             return ;
30095         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30096         //{
30097             // We must check the elements firstly using the Id and then the name.
30098         var oTextarea = document.getElementById( this.getId() );
30099         
30100         var colElementsByName = document.getElementsByName( this.getId() ) ;
30101          
30102         oTextarea.style.display = 'none' ;
30103
30104         if ( oTextarea.tabIndex ) {            
30105             this.TabIndex = oTextarea.tabIndex ;
30106         }
30107         
30108         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30109         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30110         this.frame = Roo.get(this.getId() + '___Frame')
30111     },
30112     
30113     _getConfigHtml : function()
30114     {
30115         var sConfig = '' ;
30116
30117         for ( var o in this.fckconfig ) {
30118             sConfig += sConfig.length > 0  ? '&amp;' : '';
30119             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30120         }
30121
30122         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30123     },
30124     
30125     
30126     _getIFrameHtml : function()
30127     {
30128         var sFile = 'fckeditor.html' ;
30129         /* no idea what this is about..
30130         try
30131         {
30132             if ( (/fcksource=true/i).test( window.top.location.search ) )
30133                 sFile = 'fckeditor.original.html' ;
30134         }
30135         catch (e) { 
30136         */
30137
30138         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30139         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30140         
30141         
30142         var html = '<iframe id="' + this.getId() +
30143             '___Frame" src="' + sLink +
30144             '" width="' + this.width +
30145             '" height="' + this.height + '"' +
30146             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30147             ' frameborder="0" scrolling="no"></iframe>' ;
30148
30149         return html ;
30150     },
30151     
30152     _insertHtmlBefore : function( html, element )
30153     {
30154         if ( element.insertAdjacentHTML )       {
30155             // IE
30156             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30157         } else { // Gecko
30158             var oRange = document.createRange() ;
30159             oRange.setStartBefore( element ) ;
30160             var oFragment = oRange.createContextualFragment( html );
30161             element.parentNode.insertBefore( oFragment, element ) ;
30162         }
30163     }
30164     
30165     
30166   
30167     
30168     
30169     
30170     
30171
30172 });
30173
30174 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30175
30176 function FCKeditor_OnComplete(editorInstance){
30177     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30178     f.fckEditor = editorInstance;
30179     //console.log("loaded");
30180     f.fireEvent('editorinit', f, editorInstance);
30181
30182   
30183
30184  
30185
30186
30187
30188
30189
30190
30191
30192
30193
30194
30195
30196
30197
30198
30199
30200 //<script type="text/javascript">
30201 /**
30202  * @class Roo.form.GridField
30203  * @extends Roo.form.Field
30204  * Embed a grid (or editable grid into a form)
30205  * STATUS ALPHA
30206  * 
30207  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30208  * it needs 
30209  * xgrid.store = Roo.data.Store
30210  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30211  * xgrid.store.reader = Roo.data.JsonReader 
30212  * 
30213  * 
30214  * @constructor
30215  * Creates a new GridField
30216  * @param {Object} config Configuration options
30217  */
30218 Roo.form.GridField = function(config){
30219     Roo.form.GridField.superclass.constructor.call(this, config);
30220      
30221 };
30222
30223 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30224     /**
30225      * @cfg {Number} width  - used to restrict width of grid..
30226      */
30227     width : 100,
30228     /**
30229      * @cfg {Number} height - used to restrict height of grid..
30230      */
30231     height : 50,
30232      /**
30233      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30234          * 
30235          *}
30236      */
30237     xgrid : false, 
30238     /**
30239      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30240      * {tag: "input", type: "checkbox", autocomplete: "off"})
30241      */
30242    // defaultAutoCreate : { tag: 'div' },
30243     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30244     /**
30245      * @cfg {String} addTitle Text to include for adding a title.
30246      */
30247     addTitle : false,
30248     //
30249     onResize : function(){
30250         Roo.form.Field.superclass.onResize.apply(this, arguments);
30251     },
30252
30253     initEvents : function(){
30254         // Roo.form.Checkbox.superclass.initEvents.call(this);
30255         // has no events...
30256        
30257     },
30258
30259
30260     getResizeEl : function(){
30261         return this.wrap;
30262     },
30263
30264     getPositionEl : function(){
30265         return this.wrap;
30266     },
30267
30268     // private
30269     onRender : function(ct, position){
30270         
30271         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30272         var style = this.style;
30273         delete this.style;
30274         
30275         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30276         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30277         this.viewEl = this.wrap.createChild({ tag: 'div' });
30278         if (style) {
30279             this.viewEl.applyStyles(style);
30280         }
30281         if (this.width) {
30282             this.viewEl.setWidth(this.width);
30283         }
30284         if (this.height) {
30285             this.viewEl.setHeight(this.height);
30286         }
30287         //if(this.inputValue !== undefined){
30288         //this.setValue(this.value);
30289         
30290         
30291         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30292         
30293         
30294         this.grid.render();
30295         this.grid.getDataSource().on('remove', this.refreshValue, this);
30296         this.grid.getDataSource().on('update', this.refreshValue, this);
30297         this.grid.on('afteredit', this.refreshValue, this);
30298  
30299     },
30300      
30301     
30302     /**
30303      * Sets the value of the item. 
30304      * @param {String} either an object  or a string..
30305      */
30306     setValue : function(v){
30307         //this.value = v;
30308         v = v || []; // empty set..
30309         // this does not seem smart - it really only affects memoryproxy grids..
30310         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30311             var ds = this.grid.getDataSource();
30312             // assumes a json reader..
30313             var data = {}
30314             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30315             ds.loadData( data);
30316         }
30317         // clear selection so it does not get stale.
30318         if (this.grid.sm) { 
30319             this.grid.sm.clearSelections();
30320         }
30321         
30322         Roo.form.GridField.superclass.setValue.call(this, v);
30323         this.refreshValue();
30324         // should load data in the grid really....
30325     },
30326     
30327     // private
30328     refreshValue: function() {
30329          var val = [];
30330         this.grid.getDataSource().each(function(r) {
30331             val.push(r.data);
30332         });
30333         this.el.dom.value = Roo.encode(val);
30334     }
30335     
30336      
30337     
30338     
30339 });/*
30340  * Based on:
30341  * Ext JS Library 1.1.1
30342  * Copyright(c) 2006-2007, Ext JS, LLC.
30343  *
30344  * Originally Released Under LGPL - original licence link has changed is not relivant.
30345  *
30346  * Fork - LGPL
30347  * <script type="text/javascript">
30348  */
30349 /**
30350  * @class Roo.form.DisplayField
30351  * @extends Roo.form.Field
30352  * A generic Field to display non-editable data.
30353  * @constructor
30354  * Creates a new Display Field item.
30355  * @param {Object} config Configuration options
30356  */
30357 Roo.form.DisplayField = function(config){
30358     Roo.form.DisplayField.superclass.constructor.call(this, config);
30359     
30360 };
30361
30362 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30363     inputType:      'hidden',
30364     allowBlank:     true,
30365     readOnly:         true,
30366     
30367  
30368     /**
30369      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30370      */
30371     focusClass : undefined,
30372     /**
30373      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30374      */
30375     fieldClass: 'x-form-field',
30376     
30377      /**
30378      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30379      */
30380     valueRenderer: undefined,
30381     
30382     width: 100,
30383     /**
30384      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30385      * {tag: "input", type: "checkbox", autocomplete: "off"})
30386      */
30387      
30388  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30389
30390     onResize : function(){
30391         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30392         
30393     },
30394
30395     initEvents : function(){
30396         // Roo.form.Checkbox.superclass.initEvents.call(this);
30397         // has no events...
30398        
30399     },
30400
30401
30402     getResizeEl : function(){
30403         return this.wrap;
30404     },
30405
30406     getPositionEl : function(){
30407         return this.wrap;
30408     },
30409
30410     // private
30411     onRender : function(ct, position){
30412         
30413         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30414         //if(this.inputValue !== undefined){
30415         this.wrap = this.el.wrap();
30416         
30417         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30418         
30419         if (this.bodyStyle) {
30420             this.viewEl.applyStyles(this.bodyStyle);
30421         }
30422         //this.viewEl.setStyle('padding', '2px');
30423         
30424         this.setValue(this.value);
30425         
30426     },
30427 /*
30428     // private
30429     initValue : Roo.emptyFn,
30430
30431   */
30432
30433         // private
30434     onClick : function(){
30435         
30436     },
30437
30438     /**
30439      * Sets the checked state of the checkbox.
30440      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30441      */
30442     setValue : function(v){
30443         this.value = v;
30444         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30445         // this might be called before we have a dom element..
30446         if (!this.viewEl) {
30447             return;
30448         }
30449         this.viewEl.dom.innerHTML = html;
30450         Roo.form.DisplayField.superclass.setValue.call(this, v);
30451
30452     }
30453 });/*
30454  * 
30455  * Licence- LGPL
30456  * 
30457  */
30458
30459 /**
30460  * @class Roo.form.DayPicker
30461  * @extends Roo.form.Field
30462  * A Day picker show [M] [T] [W] ....
30463  * @constructor
30464  * Creates a new Day Picker
30465  * @param {Object} config Configuration options
30466  */
30467 Roo.form.DayPicker= function(config){
30468     Roo.form.DayPicker.superclass.constructor.call(this, config);
30469      
30470 };
30471
30472 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30473     /**
30474      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30475      */
30476     focusClass : undefined,
30477     /**
30478      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30479      */
30480     fieldClass: "x-form-field",
30481    
30482     /**
30483      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30484      * {tag: "input", type: "checkbox", autocomplete: "off"})
30485      */
30486     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30487     
30488    
30489     actionMode : 'viewEl', 
30490     //
30491     // private
30492  
30493     inputType : 'hidden',
30494     
30495      
30496     inputElement: false, // real input element?
30497     basedOn: false, // ????
30498     
30499     isFormField: true, // not sure where this is needed!!!!
30500
30501     onResize : function(){
30502         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30503         if(!this.boxLabel){
30504             this.el.alignTo(this.wrap, 'c-c');
30505         }
30506     },
30507
30508     initEvents : function(){
30509         Roo.form.Checkbox.superclass.initEvents.call(this);
30510         this.el.on("click", this.onClick,  this);
30511         this.el.on("change", this.onClick,  this);
30512     },
30513
30514
30515     getResizeEl : function(){
30516         return this.wrap;
30517     },
30518
30519     getPositionEl : function(){
30520         return this.wrap;
30521     },
30522
30523     
30524     // private
30525     onRender : function(ct, position){
30526         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30527        
30528         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30529         
30530         var r1 = '<table><tr>';
30531         var r2 = '<tr class="x-form-daypick-icons">';
30532         for (var i=0; i < 7; i++) {
30533             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30534             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30535         }
30536         
30537         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30538         viewEl.select('img').on('click', this.onClick, this);
30539         this.viewEl = viewEl;   
30540         
30541         
30542         // this will not work on Chrome!!!
30543         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30544         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30545         
30546         
30547           
30548
30549     },
30550
30551     // private
30552     initValue : Roo.emptyFn,
30553
30554     /**
30555      * Returns the checked state of the checkbox.
30556      * @return {Boolean} True if checked, else false
30557      */
30558     getValue : function(){
30559         return this.el.dom.value;
30560         
30561     },
30562
30563         // private
30564     onClick : function(e){ 
30565         //this.setChecked(!this.checked);
30566         Roo.get(e.target).toggleClass('x-menu-item-checked');
30567         this.refreshValue();
30568         //if(this.el.dom.checked != this.checked){
30569         //    this.setValue(this.el.dom.checked);
30570        // }
30571     },
30572     
30573     // private
30574     refreshValue : function()
30575     {
30576         var val = '';
30577         this.viewEl.select('img',true).each(function(e,i,n)  {
30578             val += e.is(".x-menu-item-checked") ? String(n) : '';
30579         });
30580         this.setValue(val, true);
30581     },
30582
30583     /**
30584      * Sets the checked state of the checkbox.
30585      * On is always based on a string comparison between inputValue and the param.
30586      * @param {Boolean/String} value - the value to set 
30587      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30588      */
30589     setValue : function(v,suppressEvent){
30590         if (!this.el.dom) {
30591             return;
30592         }
30593         var old = this.el.dom.value ;
30594         this.el.dom.value = v;
30595         if (suppressEvent) {
30596             return ;
30597         }
30598          
30599         // update display..
30600         this.viewEl.select('img',true).each(function(e,i,n)  {
30601             
30602             var on = e.is(".x-menu-item-checked");
30603             var newv = v.indexOf(String(n)) > -1;
30604             if (on != newv) {
30605                 e.toggleClass('x-menu-item-checked');
30606             }
30607             
30608         });
30609         
30610         
30611         this.fireEvent('change', this, v, old);
30612         
30613         
30614     },
30615    
30616     // handle setting of hidden value by some other method!!?!?
30617     setFromHidden: function()
30618     {
30619         if(!this.el){
30620             return;
30621         }
30622         //console.log("SET FROM HIDDEN");
30623         //alert('setFrom hidden');
30624         this.setValue(this.el.dom.value);
30625     },
30626     
30627     onDestroy : function()
30628     {
30629         if(this.viewEl){
30630             Roo.get(this.viewEl).remove();
30631         }
30632          
30633         Roo.form.DayPicker.superclass.onDestroy.call(this);
30634     }
30635
30636 });/*
30637  * RooJS Library 1.1.1
30638  * Copyright(c) 2008-2011  Alan Knowles
30639  *
30640  * License - LGPL
30641  */
30642  
30643
30644 /**
30645  * @class Roo.form.ComboCheck
30646  * @extends Roo.form.ComboBox
30647  * A combobox for multiple select items.
30648  *
30649  * FIXME - could do with a reset button..
30650  * 
30651  * @constructor
30652  * Create a new ComboCheck
30653  * @param {Object} config Configuration options
30654  */
30655 Roo.form.ComboCheck = function(config){
30656     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30657     // should verify some data...
30658     // like
30659     // hiddenName = required..
30660     // displayField = required
30661     // valudField == required
30662     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30663     var _t = this;
30664     Roo.each(req, function(e) {
30665         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30666             throw "Roo.form.ComboCheck : missing value for: " + e;
30667         }
30668     });
30669     
30670     
30671 };
30672
30673 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30674      
30675      
30676     editable : false,
30677      
30678     selectedClass: 'x-menu-item-checked', 
30679     
30680     // private
30681     onRender : function(ct, position){
30682         var _t = this;
30683         
30684         
30685         
30686         if(!this.tpl){
30687             var cls = 'x-combo-list';
30688
30689             
30690             this.tpl =  new Roo.Template({
30691                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30692                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30693                    '<span>{' + this.displayField + '}</span>' +
30694                     '</div>' 
30695                 
30696             });
30697         }
30698  
30699         
30700         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30701         this.view.singleSelect = false;
30702         this.view.multiSelect = true;
30703         this.view.toggleSelect = true;
30704         this.pageTb.add(new Roo.Toolbar.Fill(), {
30705             
30706             text: 'Done',
30707             handler: function()
30708             {
30709                 _t.collapse();
30710             }
30711         });
30712     },
30713     
30714     onViewOver : function(e, t){
30715         // do nothing...
30716         return;
30717         
30718     },
30719     
30720     onViewClick : function(doFocus,index){
30721         return;
30722         
30723     },
30724     select: function () {
30725         //Roo.log("SELECT CALLED");
30726     },
30727      
30728     selectByValue : function(xv, scrollIntoView){
30729         var ar = this.getValueArray();
30730         var sels = [];
30731         
30732         Roo.each(ar, function(v) {
30733             if(v === undefined || v === null){
30734                 return;
30735             }
30736             var r = this.findRecord(this.valueField, v);
30737             if(r){
30738                 sels.push(this.store.indexOf(r))
30739                 
30740             }
30741         },this);
30742         this.view.select(sels);
30743         return false;
30744     },
30745     
30746     
30747     
30748     onSelect : function(record, index){
30749        // Roo.log("onselect Called");
30750        // this is only called by the clear button now..
30751         this.view.clearSelections();
30752         this.setValue('[]');
30753         if (this.value != this.valueBefore) {
30754             this.fireEvent('change', this, this.value, this.valueBefore);
30755             this.valueBefore = this.value;
30756         }
30757     },
30758     getValueArray : function()
30759     {
30760         var ar = [] ;
30761         
30762         try {
30763             //Roo.log(this.value);
30764             if (typeof(this.value) == 'undefined') {
30765                 return [];
30766             }
30767             var ar = Roo.decode(this.value);
30768             return  ar instanceof Array ? ar : []; //?? valid?
30769             
30770         } catch(e) {
30771             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30772             return [];
30773         }
30774          
30775     },
30776     expand : function ()
30777     {
30778         
30779         Roo.form.ComboCheck.superclass.expand.call(this);
30780         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30781         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30782         
30783
30784     },
30785     
30786     collapse : function(){
30787         Roo.form.ComboCheck.superclass.collapse.call(this);
30788         var sl = this.view.getSelectedIndexes();
30789         var st = this.store;
30790         var nv = [];
30791         var tv = [];
30792         var r;
30793         Roo.each(sl, function(i) {
30794             r = st.getAt(i);
30795             nv.push(r.get(this.valueField));
30796         },this);
30797         this.setValue(Roo.encode(nv));
30798         if (this.value != this.valueBefore) {
30799
30800             this.fireEvent('change', this, this.value, this.valueBefore);
30801             this.valueBefore = this.value;
30802         }
30803         
30804     },
30805     
30806     setValue : function(v){
30807         // Roo.log(v);
30808         this.value = v;
30809         
30810         var vals = this.getValueArray();
30811         var tv = [];
30812         Roo.each(vals, function(k) {
30813             var r = this.findRecord(this.valueField, k);
30814             if(r){
30815                 tv.push(r.data[this.displayField]);
30816             }else if(this.valueNotFoundText !== undefined){
30817                 tv.push( this.valueNotFoundText );
30818             }
30819         },this);
30820        // Roo.log(tv);
30821         
30822         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30823         this.hiddenField.value = v;
30824         this.value = v;
30825     }
30826     
30827 });/*
30828  * Based on:
30829  * Ext JS Library 1.1.1
30830  * Copyright(c) 2006-2007, Ext JS, LLC.
30831  *
30832  * Originally Released Under LGPL - original licence link has changed is not relivant.
30833  *
30834  * Fork - LGPL
30835  * <script type="text/javascript">
30836  */
30837  
30838 /**
30839  * @class Roo.form.Signature
30840  * @extends Roo.form.Field
30841  * Signature field.  
30842  * @constructor
30843  * 
30844  * @param {Object} config Configuration options
30845  */
30846
30847 Roo.form.Signature = function(config){
30848     Roo.form.Signature.superclass.constructor.call(this, config);
30849     
30850     this.addEvents({// not in used??
30851          /**
30852          * @event confirm
30853          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30854              * @param {Roo.form.Signature} combo This combo box
30855              */
30856         'confirm' : true,
30857         /**
30858          * @event reset
30859          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30860              * @param {Roo.form.ComboBox} combo This combo box
30861              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30862              */
30863         'reset' : true
30864     });
30865 };
30866
30867 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30868     /**
30869      * @cfg {Object} labels Label to use when rendering a form.
30870      * defaults to 
30871      * labels : { 
30872      *      clear : "Clear",
30873      *      confirm : "Confirm"
30874      *  }
30875      */
30876     labels : { 
30877         clear : "Clear",
30878         confirm : "Confirm"
30879     },
30880     /**
30881      * @cfg {Number} width The signature panel width (defaults to 300)
30882      */
30883     width: 300,
30884     /**
30885      * @cfg {Number} height The signature panel height (defaults to 100)
30886      */
30887     height : 100,
30888     /**
30889      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30890      */
30891     allowBlank : false,
30892     
30893     //private
30894     // {Object} signPanel The signature SVG panel element (defaults to {})
30895     signPanel : {},
30896     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30897     isMouseDown : false,
30898     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30899     isConfirmed : false,
30900     // {String} signatureTmp SVG mapping string (defaults to empty string)
30901     signatureTmp : '',
30902     
30903     
30904     defaultAutoCreate : { // modified by initCompnoent..
30905         tag: "input",
30906         type:"hidden"
30907     },
30908
30909     // private
30910     onRender : function(ct, position){
30911         
30912         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30913         
30914         this.wrap = this.el.wrap({
30915             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30916         });
30917         
30918         this.createToolbar(this);
30919         this.signPanel = this.wrap.createChild({
30920                 tag: 'div',
30921                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30922             }, this.el
30923         );
30924             
30925         this.svgID = Roo.id();
30926         this.svgEl = this.signPanel.createChild({
30927               xmlns : 'http://www.w3.org/2000/svg',
30928               tag : 'svg',
30929               id : this.svgID + "-svg",
30930               width: this.width,
30931               height: this.height,
30932               viewBox: '0 0 '+this.width+' '+this.height,
30933               cn : [
30934                 {
30935                     tag: "rect",
30936                     id: this.svgID + "-svg-r",
30937                     width: this.width,
30938                     height: this.height,
30939                     fill: "#ffa"
30940                 },
30941                 {
30942                     tag: "line",
30943                     id: this.svgID + "-svg-l",
30944                     x1: "0", // start
30945                     y1: (this.height*0.8), // start set the line in 80% of height
30946                     x2: this.width, // end
30947                     y2: (this.height*0.8), // end set the line in 80% of height
30948                     'stroke': "#666",
30949                     'stroke-width': "1",
30950                     'stroke-dasharray': "3",
30951                     'shape-rendering': "crispEdges",
30952                     'pointer-events': "none"
30953                 },
30954                 {
30955                     tag: "path",
30956                     id: this.svgID + "-svg-p",
30957                     'stroke': "navy",
30958                     'stroke-width': "3",
30959                     'fill': "none",
30960                     'pointer-events': 'none'
30961                 }
30962               ]
30963         });
30964         this.createSVG();
30965         this.svgBox = this.svgEl.dom.getScreenCTM();
30966     },
30967     createSVG : function(){ 
30968         var svg = this.signPanel;
30969         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30970         var t = this;
30971
30972         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30973         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30974         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30975         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30976         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30977         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30978         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30979         
30980     },
30981     isTouchEvent : function(e){
30982         return e.type.match(/^touch/);
30983     },
30984     getCoords : function (e) {
30985         var pt    = this.svgEl.dom.createSVGPoint();
30986         pt.x = e.clientX; 
30987         pt.y = e.clientY;
30988         if (this.isTouchEvent(e)) {
30989             pt.x =  e.targetTouches[0].clientX 
30990             pt.y = e.targetTouches[0].clientY;
30991         }
30992         var a = this.svgEl.dom.getScreenCTM();
30993         var b = a.inverse();
30994         var mx = pt.matrixTransform(b);
30995         return mx.x + ',' + mx.y;
30996     },
30997     //mouse event headler 
30998     down : function (e) {
30999         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31000         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31001         
31002         this.isMouseDown = true;
31003         
31004         e.preventDefault();
31005     },
31006     move : function (e) {
31007         if (this.isMouseDown) {
31008             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31009             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31010         }
31011         
31012         e.preventDefault();
31013     },
31014     up : function (e) {
31015         this.isMouseDown = false;
31016         var sp = this.signatureTmp.split(' ');
31017         
31018         if(sp.length > 1){
31019             if(!sp[sp.length-2].match(/^L/)){
31020                 sp.pop();
31021                 sp.pop();
31022                 sp.push("");
31023                 this.signatureTmp = sp.join(" ");
31024             }
31025         }
31026         if(this.getValue() != this.signatureTmp){
31027             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31028             this.isConfirmed = false;
31029         }
31030         e.preventDefault();
31031     },
31032     
31033     /**
31034      * Protected method that will not generally be called directly. It
31035      * is called when the editor creates its toolbar. Override this method if you need to
31036      * add custom toolbar buttons.
31037      * @param {HtmlEditor} editor
31038      */
31039     createToolbar : function(editor){
31040          function btn(id, toggle, handler){
31041             var xid = fid + '-'+ id ;
31042             return {
31043                 id : xid,
31044                 cmd : id,
31045                 cls : 'x-btn-icon x-edit-'+id,
31046                 enableToggle:toggle !== false,
31047                 scope: editor, // was editor...
31048                 handler:handler||editor.relayBtnCmd,
31049                 clickEvent:'mousedown',
31050                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31051                 tabIndex:-1
31052             };
31053         }
31054         
31055         
31056         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31057         this.tb = tb;
31058         this.tb.add(
31059            {
31060                 cls : ' x-signature-btn x-signature-'+id,
31061                 scope: editor, // was editor...
31062                 handler: this.reset,
31063                 clickEvent:'mousedown',
31064                 text: this.labels.clear
31065             },
31066             {
31067                  xtype : 'Fill',
31068                  xns: Roo.Toolbar
31069             }, 
31070             {
31071                 cls : '  x-signature-btn x-signature-'+id,
31072                 scope: editor, // was editor...
31073                 handler: this.confirmHandler,
31074                 clickEvent:'mousedown',
31075                 text: this.labels.confirm
31076             }
31077         );
31078     
31079     },
31080     //public
31081     /**
31082      * when user is clicked confirm then show this image.....
31083      * 
31084      * @return {String} Image Data URI
31085      */
31086     getImageDataURI : function(){
31087         var svg = this.svgEl.dom.parentNode.innerHTML;
31088         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31089         return src; 
31090     },
31091     /**
31092      * 
31093      * @return {Boolean} this.isConfirmed
31094      */
31095     getConfirmed : function(){
31096         return this.isConfirmed;
31097     },
31098     /**
31099      * 
31100      * @return {Number} this.width
31101      */
31102     getWidth : function(){
31103         return this.width;
31104     },
31105     /**
31106      * 
31107      * @return {Number} this.height
31108      */
31109     getHeight : function(){
31110         return this.height;
31111     },
31112     // private
31113     getSignature : function(){
31114         return this.signatureTmp;
31115     },
31116     // private
31117     reset : function(){
31118         this.signatureTmp = '';
31119         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31120         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31121         this.isConfirmed = false;
31122         Roo.form.Signature.superclass.reset.call(this);
31123     },
31124     setSignature : function(s){
31125         this.signatureTmp = s;
31126         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31127         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31128         this.setValue(s);
31129         this.isConfirmed = false;
31130         Roo.form.Signature.superclass.reset.call(this);
31131     }, 
31132     test : function(){
31133 //        Roo.log(this.signPanel.dom.contentWindow.up())
31134     },
31135     //private
31136     setConfirmed : function(){
31137         
31138         
31139         
31140 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31141     },
31142     // private
31143     confirmHandler : function(){
31144         if(!this.getSignature()){
31145             return;
31146         }
31147         
31148         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31149         this.setValue(this.getSignature());
31150         this.isConfirmed = true;
31151         
31152         this.fireEvent('confirm', this);
31153     },
31154     // private
31155     // Subclasses should provide the validation implementation by overriding this
31156     validateValue : function(value){
31157         if(this.allowBlank){
31158             return true;
31159         }
31160         
31161         if(this.isConfirmed){
31162             return true;
31163         }
31164         return false;
31165     }
31166 });/*
31167  * Based on:
31168  * Ext JS Library 1.1.1
31169  * Copyright(c) 2006-2007, Ext JS, LLC.
31170  *
31171  * Originally Released Under LGPL - original licence link has changed is not relivant.
31172  *
31173  * Fork - LGPL
31174  * <script type="text/javascript">
31175  */
31176  
31177
31178 /**
31179  * @class Roo.form.ComboBox
31180  * @extends Roo.form.TriggerField
31181  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31182  * @constructor
31183  * Create a new ComboBox.
31184  * @param {Object} config Configuration options
31185  */
31186 Roo.form.Select = function(config){
31187     Roo.form.Select.superclass.constructor.call(this, config);
31188      
31189 };
31190
31191 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31192     /**
31193      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31194      */
31195     /**
31196      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31197      * rendering into an Roo.Editor, defaults to false)
31198      */
31199     /**
31200      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31201      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31202      */
31203     /**
31204      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31205      */
31206     /**
31207      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31208      * the dropdown list (defaults to undefined, with no header element)
31209      */
31210
31211      /**
31212      * @cfg {String/Roo.Template} tpl The template to use to render the output
31213      */
31214      
31215     // private
31216     defaultAutoCreate : {tag: "select"  },
31217     /**
31218      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31219      */
31220     listWidth: undefined,
31221     /**
31222      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31223      * mode = 'remote' or 'text' if mode = 'local')
31224      */
31225     displayField: undefined,
31226     /**
31227      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31228      * mode = 'remote' or 'value' if mode = 'local'). 
31229      * Note: use of a valueField requires the user make a selection
31230      * in order for a value to be mapped.
31231      */
31232     valueField: undefined,
31233     
31234     
31235     /**
31236      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31237      * field's data value (defaults to the underlying DOM element's name)
31238      */
31239     hiddenName: undefined,
31240     /**
31241      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31242      */
31243     listClass: '',
31244     /**
31245      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31246      */
31247     selectedClass: 'x-combo-selected',
31248     /**
31249      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31250      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31251      * which displays a downward arrow icon).
31252      */
31253     triggerClass : 'x-form-arrow-trigger',
31254     /**
31255      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31256      */
31257     shadow:'sides',
31258     /**
31259      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31260      * anchor positions (defaults to 'tl-bl')
31261      */
31262     listAlign: 'tl-bl?',
31263     /**
31264      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31265      */
31266     maxHeight: 300,
31267     /**
31268      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31269      * query specified by the allQuery config option (defaults to 'query')
31270      */
31271     triggerAction: 'query',
31272     /**
31273      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31274      * (defaults to 4, does not apply if editable = false)
31275      */
31276     minChars : 4,
31277     /**
31278      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31279      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31280      */
31281     typeAhead: false,
31282     /**
31283      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31284      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31285      */
31286     queryDelay: 500,
31287     /**
31288      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31289      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31290      */
31291     pageSize: 0,
31292     /**
31293      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31294      * when editable = true (defaults to false)
31295      */
31296     selectOnFocus:false,
31297     /**
31298      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31299      */
31300     queryParam: 'query',
31301     /**
31302      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31303      * when mode = 'remote' (defaults to 'Loading...')
31304      */
31305     loadingText: 'Loading...',
31306     /**
31307      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31308      */
31309     resizable: false,
31310     /**
31311      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31312      */
31313     handleHeight : 8,
31314     /**
31315      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31316      * traditional select (defaults to true)
31317      */
31318     editable: true,
31319     /**
31320      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31321      */
31322     allQuery: '',
31323     /**
31324      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31325      */
31326     mode: 'remote',
31327     /**
31328      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31329      * listWidth has a higher value)
31330      */
31331     minListWidth : 70,
31332     /**
31333      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31334      * allow the user to set arbitrary text into the field (defaults to false)
31335      */
31336     forceSelection:false,
31337     /**
31338      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31339      * if typeAhead = true (defaults to 250)
31340      */
31341     typeAheadDelay : 250,
31342     /**
31343      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31344      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31345      */
31346     valueNotFoundText : undefined,
31347     
31348     /**
31349      * @cfg {String} defaultValue The value displayed after loading the store.
31350      */
31351     defaultValue: '',
31352     
31353     /**
31354      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31355      */
31356     blockFocus : false,
31357     
31358     /**
31359      * @cfg {Boolean} disableClear Disable showing of clear button.
31360      */
31361     disableClear : false,
31362     /**
31363      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31364      */
31365     alwaysQuery : false,
31366     
31367     //private
31368     addicon : false,
31369     editicon: false,
31370     
31371     // element that contains real text value.. (when hidden is used..)
31372      
31373     // private
31374     onRender : function(ct, position){
31375         Roo.form.Field.prototype.onRender.call(this, ct, position);
31376         
31377         if(this.store){
31378             this.store.on('beforeload', this.onBeforeLoad, this);
31379             this.store.on('load', this.onLoad, this);
31380             this.store.on('loadexception', this.onLoadException, this);
31381             this.store.load({});
31382         }
31383         
31384         
31385         
31386     },
31387
31388     // private
31389     initEvents : function(){
31390         //Roo.form.ComboBox.superclass.initEvents.call(this);
31391  
31392     },
31393
31394     onDestroy : function(){
31395        
31396         if(this.store){
31397             this.store.un('beforeload', this.onBeforeLoad, this);
31398             this.store.un('load', this.onLoad, this);
31399             this.store.un('loadexception', this.onLoadException, this);
31400         }
31401         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31402     },
31403
31404     // private
31405     fireKey : function(e){
31406         if(e.isNavKeyPress() && !this.list.isVisible()){
31407             this.fireEvent("specialkey", this, e);
31408         }
31409     },
31410
31411     // private
31412     onResize: function(w, h){
31413         
31414         return; 
31415     
31416         
31417     },
31418
31419     /**
31420      * Allow or prevent the user from directly editing the field text.  If false is passed,
31421      * the user will only be able to select from the items defined in the dropdown list.  This method
31422      * is the runtime equivalent of setting the 'editable' config option at config time.
31423      * @param {Boolean} value True to allow the user to directly edit the field text
31424      */
31425     setEditable : function(value){
31426          
31427     },
31428
31429     // private
31430     onBeforeLoad : function(){
31431         
31432         Roo.log("Select before load");
31433         return;
31434     
31435         this.innerList.update(this.loadingText ?
31436                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31437         //this.restrictHeight();
31438         this.selectedIndex = -1;
31439     },
31440
31441     // private
31442     onLoad : function(){
31443
31444     
31445         var dom = this.el.dom;
31446         dom.innerHTML = '';
31447          var od = dom.ownerDocument;
31448          
31449         if (this.emptyText) {
31450             var op = od.createElement('option');
31451             op.setAttribute('value', '');
31452             op.innerHTML = String.format('{0}', this.emptyText);
31453             dom.appendChild(op);
31454         }
31455         if(this.store.getCount() > 0){
31456            
31457             var vf = this.valueField;
31458             var df = this.displayField;
31459             this.store.data.each(function(r) {
31460                 // which colmsn to use... testing - cdoe / title..
31461                 var op = od.createElement('option');
31462                 op.setAttribute('value', r.data[vf]);
31463                 op.innerHTML = String.format('{0}', r.data[df]);
31464                 dom.appendChild(op);
31465             });
31466             if (typeof(this.defaultValue != 'undefined')) {
31467                 this.setValue(this.defaultValue);
31468             }
31469             
31470              
31471         }else{
31472             //this.onEmptyResults();
31473         }
31474         //this.el.focus();
31475     },
31476     // private
31477     onLoadException : function()
31478     {
31479         dom.innerHTML = '';
31480             
31481         Roo.log("Select on load exception");
31482         return;
31483     
31484         this.collapse();
31485         Roo.log(this.store.reader.jsonData);
31486         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31487             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31488         }
31489         
31490         
31491     },
31492     // private
31493     onTypeAhead : function(){
31494          
31495     },
31496
31497     // private
31498     onSelect : function(record, index){
31499         Roo.log('on select?');
31500         return;
31501         if(this.fireEvent('beforeselect', this, record, index) !== false){
31502             this.setFromData(index > -1 ? record.data : false);
31503             this.collapse();
31504             this.fireEvent('select', this, record, index);
31505         }
31506     },
31507
31508     /**
31509      * Returns the currently selected field value or empty string if no value is set.
31510      * @return {String} value The selected value
31511      */
31512     getValue : function(){
31513         var dom = this.el.dom;
31514         this.value = dom.options[dom.selectedIndex].value;
31515         return this.value;
31516         
31517     },
31518
31519     /**
31520      * Clears any text/value currently set in the field
31521      */
31522     clearValue : function(){
31523         this.value = '';
31524         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31525         
31526     },
31527
31528     /**
31529      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31530      * will be displayed in the field.  If the value does not match the data value of an existing item,
31531      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31532      * Otherwise the field will be blank (although the value will still be set).
31533      * @param {String} value The value to match
31534      */
31535     setValue : function(v){
31536         var d = this.el.dom;
31537         for (var i =0; i < d.options.length;i++) {
31538             if (v == d.options[i].value) {
31539                 d.selectedIndex = i;
31540                 this.value = v;
31541                 return;
31542             }
31543         }
31544         this.clearValue();
31545     },
31546     /**
31547      * @property {Object} the last set data for the element
31548      */
31549     
31550     lastData : false,
31551     /**
31552      * Sets the value of the field based on a object which is related to the record format for the store.
31553      * @param {Object} value the value to set as. or false on reset?
31554      */
31555     setFromData : function(o){
31556         Roo.log('setfrom data?');
31557          
31558         
31559         
31560     },
31561     // private
31562     reset : function(){
31563         this.clearValue();
31564     },
31565     // private
31566     findRecord : function(prop, value){
31567         
31568         return false;
31569     
31570         var record;
31571         if(this.store.getCount() > 0){
31572             this.store.each(function(r){
31573                 if(r.data[prop] == value){
31574                     record = r;
31575                     return false;
31576                 }
31577                 return true;
31578             });
31579         }
31580         return record;
31581     },
31582     
31583     getName: function()
31584     {
31585         // returns hidden if it's set..
31586         if (!this.rendered) {return ''};
31587         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31588         
31589     },
31590      
31591
31592     
31593
31594     // private
31595     onEmptyResults : function(){
31596         Roo.log('empty results');
31597         //this.collapse();
31598     },
31599
31600     /**
31601      * Returns true if the dropdown list is expanded, else false.
31602      */
31603     isExpanded : function(){
31604         return false;
31605     },
31606
31607     /**
31608      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31610      * @param {String} value The data value of the item to select
31611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31612      * selected item if it is not currently in view (defaults to true)
31613      * @return {Boolean} True if the value matched an item in the list, else false
31614      */
31615     selectByValue : function(v, scrollIntoView){
31616         Roo.log('select By Value');
31617         return false;
31618     
31619         if(v !== undefined && v !== null){
31620             var r = this.findRecord(this.valueField || this.displayField, v);
31621             if(r){
31622                 this.select(this.store.indexOf(r), scrollIntoView);
31623                 return true;
31624             }
31625         }
31626         return false;
31627     },
31628
31629     /**
31630      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31631      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31632      * @param {Number} index The zero-based index of the list item to select
31633      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31634      * selected item if it is not currently in view (defaults to true)
31635      */
31636     select : function(index, scrollIntoView){
31637         Roo.log('select ');
31638         return  ;
31639         
31640         this.selectedIndex = index;
31641         this.view.select(index);
31642         if(scrollIntoView !== false){
31643             var el = this.view.getNode(index);
31644             if(el){
31645                 this.innerList.scrollChildIntoView(el, false);
31646             }
31647         }
31648     },
31649
31650       
31651
31652     // private
31653     validateBlur : function(){
31654         
31655         return;
31656         
31657     },
31658
31659     // private
31660     initQuery : function(){
31661         this.doQuery(this.getRawValue());
31662     },
31663
31664     // private
31665     doForce : function(){
31666         if(this.el.dom.value.length > 0){
31667             this.el.dom.value =
31668                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31669              
31670         }
31671     },
31672
31673     /**
31674      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31675      * query allowing the query action to be canceled if needed.
31676      * @param {String} query The SQL query to execute
31677      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31678      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31679      * saved in the current store (defaults to false)
31680      */
31681     doQuery : function(q, forceAll){
31682         
31683         Roo.log('doQuery?');
31684         if(q === undefined || q === null){
31685             q = '';
31686         }
31687         var qe = {
31688             query: q,
31689             forceAll: forceAll,
31690             combo: this,
31691             cancel:false
31692         };
31693         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31694             return false;
31695         }
31696         q = qe.query;
31697         forceAll = qe.forceAll;
31698         if(forceAll === true || (q.length >= this.minChars)){
31699             if(this.lastQuery != q || this.alwaysQuery){
31700                 this.lastQuery = q;
31701                 if(this.mode == 'local'){
31702                     this.selectedIndex = -1;
31703                     if(forceAll){
31704                         this.store.clearFilter();
31705                     }else{
31706                         this.store.filter(this.displayField, q);
31707                     }
31708                     this.onLoad();
31709                 }else{
31710                     this.store.baseParams[this.queryParam] = q;
31711                     this.store.load({
31712                         params: this.getParams(q)
31713                     });
31714                     this.expand();
31715                 }
31716             }else{
31717                 this.selectedIndex = -1;
31718                 this.onLoad();   
31719             }
31720         }
31721     },
31722
31723     // private
31724     getParams : function(q){
31725         var p = {};
31726         //p[this.queryParam] = q;
31727         if(this.pageSize){
31728             p.start = 0;
31729             p.limit = this.pageSize;
31730         }
31731         return p;
31732     },
31733
31734     /**
31735      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31736      */
31737     collapse : function(){
31738         
31739     },
31740
31741     // private
31742     collapseIf : function(e){
31743         
31744     },
31745
31746     /**
31747      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31748      */
31749     expand : function(){
31750         
31751     } ,
31752
31753     // private
31754      
31755
31756     /** 
31757     * @cfg {Boolean} grow 
31758     * @hide 
31759     */
31760     /** 
31761     * @cfg {Number} growMin 
31762     * @hide 
31763     */
31764     /** 
31765     * @cfg {Number} growMax 
31766     * @hide 
31767     */
31768     /**
31769      * @hide
31770      * @method autoSize
31771      */
31772     
31773     setWidth : function()
31774     {
31775         
31776     },
31777     getResizeEl : function(){
31778         return this.el;
31779     }
31780 });//<script type="text/javasscript">
31781  
31782
31783 /**
31784  * @class Roo.DDView
31785  * A DnD enabled version of Roo.View.
31786  * @param {Element/String} container The Element in which to create the View.
31787  * @param {String} tpl The template string used to create the markup for each element of the View
31788  * @param {Object} config The configuration properties. These include all the config options of
31789  * {@link Roo.View} plus some specific to this class.<br>
31790  * <p>
31791  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31792  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31793  * <p>
31794  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31795 .x-view-drag-insert-above {
31796         border-top:1px dotted #3366cc;
31797 }
31798 .x-view-drag-insert-below {
31799         border-bottom:1px dotted #3366cc;
31800 }
31801 </code></pre>
31802  * 
31803  */
31804  
31805 Roo.DDView = function(container, tpl, config) {
31806     Roo.DDView.superclass.constructor.apply(this, arguments);
31807     this.getEl().setStyle("outline", "0px none");
31808     this.getEl().unselectable();
31809     if (this.dragGroup) {
31810                 this.setDraggable(this.dragGroup.split(","));
31811     }
31812     if (this.dropGroup) {
31813                 this.setDroppable(this.dropGroup.split(","));
31814     }
31815     if (this.deletable) {
31816         this.setDeletable();
31817     }
31818     this.isDirtyFlag = false;
31819         this.addEvents({
31820                 "drop" : true
31821         });
31822 };
31823
31824 Roo.extend(Roo.DDView, Roo.View, {
31825 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31826 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31827 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31828 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31829
31830         isFormField: true,
31831
31832         reset: Roo.emptyFn,
31833         
31834         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31835
31836         validate: function() {
31837                 return true;
31838         },
31839         
31840         destroy: function() {
31841                 this.purgeListeners();
31842                 this.getEl.removeAllListeners();
31843                 this.getEl().remove();
31844                 if (this.dragZone) {
31845                         if (this.dragZone.destroy) {
31846                                 this.dragZone.destroy();
31847                         }
31848                 }
31849                 if (this.dropZone) {
31850                         if (this.dropZone.destroy) {
31851                                 this.dropZone.destroy();
31852                         }
31853                 }
31854         },
31855
31856 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31857         getName: function() {
31858                 return this.name;
31859         },
31860
31861 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31862         setValue: function(v) {
31863                 if (!this.store) {
31864                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31865                 }
31866                 var data = {};
31867                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31868                 this.store.proxy = new Roo.data.MemoryProxy(data);
31869                 this.store.load();
31870         },
31871
31872 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31873         getValue: function() {
31874                 var result = '(';
31875                 this.store.each(function(rec) {
31876                         result += rec.id + ',';
31877                 });
31878                 return result.substr(0, result.length - 1) + ')';
31879         },
31880         
31881         getIds: function() {
31882                 var i = 0, result = new Array(this.store.getCount());
31883                 this.store.each(function(rec) {
31884                         result[i++] = rec.id;
31885                 });
31886                 return result;
31887         },
31888         
31889         isDirty: function() {
31890                 return this.isDirtyFlag;
31891         },
31892
31893 /**
31894  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31895  *      whole Element becomes the target, and this causes the drop gesture to append.
31896  */
31897     getTargetFromEvent : function(e) {
31898                 var target = e.getTarget();
31899                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31900                 target = target.parentNode;
31901                 }
31902                 if (!target) {
31903                         target = this.el.dom.lastChild || this.el.dom;
31904                 }
31905                 return target;
31906     },
31907
31908 /**
31909  *      Create the drag data which consists of an object which has the property "ddel" as
31910  *      the drag proxy element. 
31911  */
31912     getDragData : function(e) {
31913         var target = this.findItemFromChild(e.getTarget());
31914                 if(target) {
31915                         this.handleSelection(e);
31916                         var selNodes = this.getSelectedNodes();
31917             var dragData = {
31918                 source: this,
31919                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31920                 nodes: selNodes,
31921                 records: []
31922                         };
31923                         var selectedIndices = this.getSelectedIndexes();
31924                         for (var i = 0; i < selectedIndices.length; i++) {
31925                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31926                         }
31927                         if (selNodes.length == 1) {
31928                                 dragData.ddel = target.cloneNode(true); // the div element
31929                         } else {
31930                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31931                                 div.className = 'multi-proxy';
31932                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31933                                         div.appendChild(selNodes[i].cloneNode(true));
31934                                 }
31935                                 dragData.ddel = div;
31936                         }
31937             //console.log(dragData)
31938             //console.log(dragData.ddel.innerHTML)
31939                         return dragData;
31940                 }
31941         //console.log('nodragData')
31942                 return false;
31943     },
31944     
31945 /**     Specify to which ddGroup items in this DDView may be dragged. */
31946     setDraggable: function(ddGroup) {
31947         if (ddGroup instanceof Array) {
31948                 Roo.each(ddGroup, this.setDraggable, this);
31949                 return;
31950         }
31951         if (this.dragZone) {
31952                 this.dragZone.addToGroup(ddGroup);
31953         } else {
31954                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31955                                 containerScroll: true,
31956                                 ddGroup: ddGroup 
31957
31958                         });
31959 //                      Draggability implies selection. DragZone's mousedown selects the element.
31960                         if (!this.multiSelect) { this.singleSelect = true; }
31961
31962 //                      Wire the DragZone's handlers up to methods in *this*
31963                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31964                 }
31965     },
31966
31967 /**     Specify from which ddGroup this DDView accepts drops. */
31968     setDroppable: function(ddGroup) {
31969         if (ddGroup instanceof Array) {
31970                 Roo.each(ddGroup, this.setDroppable, this);
31971                 return;
31972         }
31973         if (this.dropZone) {
31974                 this.dropZone.addToGroup(ddGroup);
31975         } else {
31976                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31977                                 containerScroll: true,
31978                                 ddGroup: ddGroup
31979                         });
31980
31981 //                      Wire the DropZone's handlers up to methods in *this*
31982                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31983                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31984                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31985                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31986                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31987                 }
31988     },
31989
31990 /**     Decide whether to drop above or below a View node. */
31991     getDropPoint : function(e, n, dd){
31992         if (n == this.el.dom) { return "above"; }
31993                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31994                 var c = t + (b - t) / 2;
31995                 var y = Roo.lib.Event.getPageY(e);
31996                 if(y <= c) {
31997                         return "above";
31998                 }else{
31999                         return "below";
32000                 }
32001     },
32002
32003     onNodeEnter : function(n, dd, e, data){
32004                 return false;
32005     },
32006     
32007     onNodeOver : function(n, dd, e, data){
32008                 var pt = this.getDropPoint(e, n, dd);
32009                 // set the insert point style on the target node
32010                 var dragElClass = this.dropNotAllowed;
32011                 if (pt) {
32012                         var targetElClass;
32013                         if (pt == "above"){
32014                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32015                                 targetElClass = "x-view-drag-insert-above";
32016                         } else {
32017                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32018                                 targetElClass = "x-view-drag-insert-below";
32019                         }
32020                         if (this.lastInsertClass != targetElClass){
32021                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32022                                 this.lastInsertClass = targetElClass;
32023                         }
32024                 }
32025                 return dragElClass;
32026         },
32027
32028     onNodeOut : function(n, dd, e, data){
32029                 this.removeDropIndicators(n);
32030     },
32031
32032     onNodeDrop : function(n, dd, e, data){
32033         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32034                 return false;
32035         }
32036         var pt = this.getDropPoint(e, n, dd);
32037                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32038                 if (pt == "below") { insertAt++; }
32039                 for (var i = 0; i < data.records.length; i++) {
32040                         var r = data.records[i];
32041                         var dup = this.store.getById(r.id);
32042                         if (dup && (dd != this.dragZone)) {
32043                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32044                         } else {
32045                                 if (data.copy) {
32046                                         this.store.insert(insertAt++, r.copy());
32047                                 } else {
32048                                         data.source.isDirtyFlag = true;
32049                                         r.store.remove(r);
32050                                         this.store.insert(insertAt++, r);
32051                                 }
32052                                 this.isDirtyFlag = true;
32053                         }
32054                 }
32055                 this.dragZone.cachedTarget = null;
32056                 return true;
32057     },
32058
32059     removeDropIndicators : function(n){
32060                 if(n){
32061                         Roo.fly(n).removeClass([
32062                                 "x-view-drag-insert-above",
32063                                 "x-view-drag-insert-below"]);
32064                         this.lastInsertClass = "_noclass";
32065                 }
32066     },
32067
32068 /**
32069  *      Utility method. Add a delete option to the DDView's context menu.
32070  *      @param {String} imageUrl The URL of the "delete" icon image.
32071  */
32072         setDeletable: function(imageUrl) {
32073                 if (!this.singleSelect && !this.multiSelect) {
32074                         this.singleSelect = true;
32075                 }
32076                 var c = this.getContextMenu();
32077                 this.contextMenu.on("itemclick", function(item) {
32078                         switch (item.id) {
32079                                 case "delete":
32080                                         this.remove(this.getSelectedIndexes());
32081                                         break;
32082                         }
32083                 }, this);
32084                 this.contextMenu.add({
32085                         icon: imageUrl,
32086                         id: "delete",
32087                         text: 'Delete'
32088                 });
32089         },
32090         
32091 /**     Return the context menu for this DDView. */
32092         getContextMenu: function() {
32093                 if (!this.contextMenu) {
32094 //                      Create the View's context menu
32095                         this.contextMenu = new Roo.menu.Menu({
32096                                 id: this.id + "-contextmenu"
32097                         });
32098                         this.el.on("contextmenu", this.showContextMenu, this);
32099                 }
32100                 return this.contextMenu;
32101         },
32102         
32103         disableContextMenu: function() {
32104                 if (this.contextMenu) {
32105                         this.el.un("contextmenu", this.showContextMenu, this);
32106                 }
32107         },
32108
32109         showContextMenu: function(e, item) {
32110         item = this.findItemFromChild(e.getTarget());
32111                 if (item) {
32112                         e.stopEvent();
32113                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32114                         this.contextMenu.showAt(e.getXY());
32115             }
32116     },
32117
32118 /**
32119  *      Remove {@link Roo.data.Record}s at the specified indices.
32120  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32121  */
32122     remove: function(selectedIndices) {
32123                 selectedIndices = [].concat(selectedIndices);
32124                 for (var i = 0; i < selectedIndices.length; i++) {
32125                         var rec = this.store.getAt(selectedIndices[i]);
32126                         this.store.remove(rec);
32127                 }
32128     },
32129
32130 /**
32131  *      Double click fires the event, but also, if this is draggable, and there is only one other
32132  *      related DropZone, it transfers the selected node.
32133  */
32134     onDblClick : function(e){
32135         var item = this.findItemFromChild(e.getTarget());
32136         if(item){
32137             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32138                 return false;
32139             }
32140             if (this.dragGroup) {
32141                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32142                     while (targets.indexOf(this.dropZone) > -1) {
32143                             targets.remove(this.dropZone);
32144                                 }
32145                     if (targets.length == 1) {
32146                                         this.dragZone.cachedTarget = null;
32147                         var el = Roo.get(targets[0].getEl());
32148                         var box = el.getBox(true);
32149                         targets[0].onNodeDrop(el.dom, {
32150                                 target: el.dom,
32151                                 xy: [box.x, box.y + box.height - 1]
32152                         }, null, this.getDragData(e));
32153                     }
32154                 }
32155         }
32156     },
32157     
32158     handleSelection: function(e) {
32159                 this.dragZone.cachedTarget = null;
32160         var item = this.findItemFromChild(e.getTarget());
32161         if (!item) {
32162                 this.clearSelections(true);
32163                 return;
32164         }
32165                 if (item && (this.multiSelect || this.singleSelect)){
32166                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32167                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32168                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32169                                 this.unselect(item);
32170                         } else {
32171                                 this.select(item, this.multiSelect && e.ctrlKey);
32172                                 this.lastSelection = item;
32173                         }
32174                 }
32175     },
32176
32177     onItemClick : function(item, index, e){
32178                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32179                         return false;
32180                 }
32181                 return true;
32182     },
32183
32184     unselect : function(nodeInfo, suppressEvent){
32185                 var node = this.getNode(nodeInfo);
32186                 if(node && this.isSelected(node)){
32187                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32188                                 Roo.fly(node).removeClass(this.selectedClass);
32189                                 this.selections.remove(node);
32190                                 if(!suppressEvent){
32191                                         this.fireEvent("selectionchange", this, this.selections);
32192                                 }
32193                         }
32194                 }
32195     }
32196 });
32197 /*
32198  * Based on:
32199  * Ext JS Library 1.1.1
32200  * Copyright(c) 2006-2007, Ext JS, LLC.
32201  *
32202  * Originally Released Under LGPL - original licence link has changed is not relivant.
32203  *
32204  * Fork - LGPL
32205  * <script type="text/javascript">
32206  */
32207  
32208 /**
32209  * @class Roo.LayoutManager
32210  * @extends Roo.util.Observable
32211  * Base class for layout managers.
32212  */
32213 Roo.LayoutManager = function(container, config){
32214     Roo.LayoutManager.superclass.constructor.call(this);
32215     this.el = Roo.get(container);
32216     // ie scrollbar fix
32217     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32218         document.body.scroll = "no";
32219     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32220         this.el.position('relative');
32221     }
32222     this.id = this.el.id;
32223     this.el.addClass("x-layout-container");
32224     /** false to disable window resize monitoring @type Boolean */
32225     this.monitorWindowResize = true;
32226     this.regions = {};
32227     this.addEvents({
32228         /**
32229          * @event layout
32230          * Fires when a layout is performed. 
32231          * @param {Roo.LayoutManager} this
32232          */
32233         "layout" : true,
32234         /**
32235          * @event regionresized
32236          * Fires when the user resizes a region. 
32237          * @param {Roo.LayoutRegion} region The resized region
32238          * @param {Number} newSize The new size (width for east/west, height for north/south)
32239          */
32240         "regionresized" : true,
32241         /**
32242          * @event regioncollapsed
32243          * Fires when a region is collapsed. 
32244          * @param {Roo.LayoutRegion} region The collapsed region
32245          */
32246         "regioncollapsed" : true,
32247         /**
32248          * @event regionexpanded
32249          * Fires when a region is expanded.  
32250          * @param {Roo.LayoutRegion} region The expanded region
32251          */
32252         "regionexpanded" : true
32253     });
32254     this.updating = false;
32255     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32256 };
32257
32258 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32259     /**
32260      * Returns true if this layout is currently being updated
32261      * @return {Boolean}
32262      */
32263     isUpdating : function(){
32264         return this.updating; 
32265     },
32266     
32267     /**
32268      * Suspend the LayoutManager from doing auto-layouts while
32269      * making multiple add or remove calls
32270      */
32271     beginUpdate : function(){
32272         this.updating = true;    
32273     },
32274     
32275     /**
32276      * Restore auto-layouts and optionally disable the manager from performing a layout
32277      * @param {Boolean} noLayout true to disable a layout update 
32278      */
32279     endUpdate : function(noLayout){
32280         this.updating = false;
32281         if(!noLayout){
32282             this.layout();
32283         }    
32284     },
32285     
32286     layout: function(){
32287         
32288     },
32289     
32290     onRegionResized : function(region, newSize){
32291         this.fireEvent("regionresized", region, newSize);
32292         this.layout();
32293     },
32294     
32295     onRegionCollapsed : function(region){
32296         this.fireEvent("regioncollapsed", region);
32297     },
32298     
32299     onRegionExpanded : function(region){
32300         this.fireEvent("regionexpanded", region);
32301     },
32302         
32303     /**
32304      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32305      * performs box-model adjustments.
32306      * @return {Object} The size as an object {width: (the width), height: (the height)}
32307      */
32308     getViewSize : function(){
32309         var size;
32310         if(this.el.dom != document.body){
32311             size = this.el.getSize();
32312         }else{
32313             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32314         }
32315         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32316         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32317         return size;
32318     },
32319     
32320     /**
32321      * Returns the Element this layout is bound to.
32322      * @return {Roo.Element}
32323      */
32324     getEl : function(){
32325         return this.el;
32326     },
32327     
32328     /**
32329      * Returns the specified region.
32330      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32331      * @return {Roo.LayoutRegion}
32332      */
32333     getRegion : function(target){
32334         return this.regions[target.toLowerCase()];
32335     },
32336     
32337     onWindowResize : function(){
32338         if(this.monitorWindowResize){
32339             this.layout();
32340         }
32341     }
32342 });/*
32343  * Based on:
32344  * Ext JS Library 1.1.1
32345  * Copyright(c) 2006-2007, Ext JS, LLC.
32346  *
32347  * Originally Released Under LGPL - original licence link has changed is not relivant.
32348  *
32349  * Fork - LGPL
32350  * <script type="text/javascript">
32351  */
32352 /**
32353  * @class Roo.BorderLayout
32354  * @extends Roo.LayoutManager
32355  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32356  * please see: <br><br>
32357  * <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>
32358  * <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>
32359  * Example:
32360  <pre><code>
32361  var layout = new Roo.BorderLayout(document.body, {
32362     north: {
32363         initialSize: 25,
32364         titlebar: false
32365     },
32366     west: {
32367         split:true,
32368         initialSize: 200,
32369         minSize: 175,
32370         maxSize: 400,
32371         titlebar: true,
32372         collapsible: true
32373     },
32374     east: {
32375         split:true,
32376         initialSize: 202,
32377         minSize: 175,
32378         maxSize: 400,
32379         titlebar: true,
32380         collapsible: true
32381     },
32382     south: {
32383         split:true,
32384         initialSize: 100,
32385         minSize: 100,
32386         maxSize: 200,
32387         titlebar: true,
32388         collapsible: true
32389     },
32390     center: {
32391         titlebar: true,
32392         autoScroll:true,
32393         resizeTabs: true,
32394         minTabWidth: 50,
32395         preferredTabWidth: 150
32396     }
32397 });
32398
32399 // shorthand
32400 var CP = Roo.ContentPanel;
32401
32402 layout.beginUpdate();
32403 layout.add("north", new CP("north", "North"));
32404 layout.add("south", new CP("south", {title: "South", closable: true}));
32405 layout.add("west", new CP("west", {title: "West"}));
32406 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32407 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32408 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32409 layout.getRegion("center").showPanel("center1");
32410 layout.endUpdate();
32411 </code></pre>
32412
32413 <b>The container the layout is rendered into can be either the body element or any other element.
32414 If it is not the body element, the container needs to either be an absolute positioned element,
32415 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32416 the container size if it is not the body element.</b>
32417
32418 * @constructor
32419 * Create a new BorderLayout
32420 * @param {String/HTMLElement/Element} container The container this layout is bound to
32421 * @param {Object} config Configuration options
32422  */
32423 Roo.BorderLayout = function(container, config){
32424     config = config || {};
32425     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32426     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32427     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32428         var target = this.factory.validRegions[i];
32429         if(config[target]){
32430             this.addRegion(target, config[target]);
32431         }
32432     }
32433 };
32434
32435 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32436     /**
32437      * Creates and adds a new region if it doesn't already exist.
32438      * @param {String} target The target region key (north, south, east, west or center).
32439      * @param {Object} config The regions config object
32440      * @return {BorderLayoutRegion} The new region
32441      */
32442     addRegion : function(target, config){
32443         if(!this.regions[target]){
32444             var r = this.factory.create(target, this, config);
32445             this.bindRegion(target, r);
32446         }
32447         return this.regions[target];
32448     },
32449
32450     // private (kinda)
32451     bindRegion : function(name, r){
32452         this.regions[name] = r;
32453         r.on("visibilitychange", this.layout, this);
32454         r.on("paneladded", this.layout, this);
32455         r.on("panelremoved", this.layout, this);
32456         r.on("invalidated", this.layout, this);
32457         r.on("resized", this.onRegionResized, this);
32458         r.on("collapsed", this.onRegionCollapsed, this);
32459         r.on("expanded", this.onRegionExpanded, this);
32460     },
32461
32462     /**
32463      * Performs a layout update.
32464      */
32465     layout : function(){
32466         if(this.updating) return;
32467         var size = this.getViewSize();
32468         var w = size.width;
32469         var h = size.height;
32470         var centerW = w;
32471         var centerH = h;
32472         var centerY = 0;
32473         var centerX = 0;
32474         //var x = 0, y = 0;
32475
32476         var rs = this.regions;
32477         var north = rs["north"];
32478         var south = rs["south"]; 
32479         var west = rs["west"];
32480         var east = rs["east"];
32481         var center = rs["center"];
32482         //if(this.hideOnLayout){ // not supported anymore
32483             //c.el.setStyle("display", "none");
32484         //}
32485         if(north && north.isVisible()){
32486             var b = north.getBox();
32487             var m = north.getMargins();
32488             b.width = w - (m.left+m.right);
32489             b.x = m.left;
32490             b.y = m.top;
32491             centerY = b.height + b.y + m.bottom;
32492             centerH -= centerY;
32493             north.updateBox(this.safeBox(b));
32494         }
32495         if(south && south.isVisible()){
32496             var b = south.getBox();
32497             var m = south.getMargins();
32498             b.width = w - (m.left+m.right);
32499             b.x = m.left;
32500             var totalHeight = (b.height + m.top + m.bottom);
32501             b.y = h - totalHeight + m.top;
32502             centerH -= totalHeight;
32503             south.updateBox(this.safeBox(b));
32504         }
32505         if(west && west.isVisible()){
32506             var b = west.getBox();
32507             var m = west.getMargins();
32508             b.height = centerH - (m.top+m.bottom);
32509             b.x = m.left;
32510             b.y = centerY + m.top;
32511             var totalWidth = (b.width + m.left + m.right);
32512             centerX += totalWidth;
32513             centerW -= totalWidth;
32514             west.updateBox(this.safeBox(b));
32515         }
32516         if(east && east.isVisible()){
32517             var b = east.getBox();
32518             var m = east.getMargins();
32519             b.height = centerH - (m.top+m.bottom);
32520             var totalWidth = (b.width + m.left + m.right);
32521             b.x = w - totalWidth + m.left;
32522             b.y = centerY + m.top;
32523             centerW -= totalWidth;
32524             east.updateBox(this.safeBox(b));
32525         }
32526         if(center){
32527             var m = center.getMargins();
32528             var centerBox = {
32529                 x: centerX + m.left,
32530                 y: centerY + m.top,
32531                 width: centerW - (m.left+m.right),
32532                 height: centerH - (m.top+m.bottom)
32533             };
32534             //if(this.hideOnLayout){
32535                 //center.el.setStyle("display", "block");
32536             //}
32537             center.updateBox(this.safeBox(centerBox));
32538         }
32539         this.el.repaint();
32540         this.fireEvent("layout", this);
32541     },
32542
32543     // private
32544     safeBox : function(box){
32545         box.width = Math.max(0, box.width);
32546         box.height = Math.max(0, box.height);
32547         return box;
32548     },
32549
32550     /**
32551      * Adds a ContentPanel (or subclass) to this layout.
32552      * @param {String} target The target region key (north, south, east, west or center).
32553      * @param {Roo.ContentPanel} panel The panel to add
32554      * @return {Roo.ContentPanel} The added panel
32555      */
32556     add : function(target, panel){
32557          
32558         target = target.toLowerCase();
32559         return this.regions[target].add(panel);
32560     },
32561
32562     /**
32563      * Remove a ContentPanel (or subclass) to this layout.
32564      * @param {String} target The target region key (north, south, east, west or center).
32565      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32566      * @return {Roo.ContentPanel} The removed panel
32567      */
32568     remove : function(target, panel){
32569         target = target.toLowerCase();
32570         return this.regions[target].remove(panel);
32571     },
32572
32573     /**
32574      * Searches all regions for a panel with the specified id
32575      * @param {String} panelId
32576      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32577      */
32578     findPanel : function(panelId){
32579         var rs = this.regions;
32580         for(var target in rs){
32581             if(typeof rs[target] != "function"){
32582                 var p = rs[target].getPanel(panelId);
32583                 if(p){
32584                     return p;
32585                 }
32586             }
32587         }
32588         return null;
32589     },
32590
32591     /**
32592      * Searches all regions for a panel with the specified id and activates (shows) it.
32593      * @param {String/ContentPanel} panelId The panels id or the panel itself
32594      * @return {Roo.ContentPanel} The shown panel or null
32595      */
32596     showPanel : function(panelId) {
32597       var rs = this.regions;
32598       for(var target in rs){
32599          var r = rs[target];
32600          if(typeof r != "function"){
32601             if(r.hasPanel(panelId)){
32602                return r.showPanel(panelId);
32603             }
32604          }
32605       }
32606       return null;
32607    },
32608
32609    /**
32610      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32611      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32612      */
32613     restoreState : function(provider){
32614         if(!provider){
32615             provider = Roo.state.Manager;
32616         }
32617         var sm = new Roo.LayoutStateManager();
32618         sm.init(this, provider);
32619     },
32620
32621     /**
32622      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32623      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32624      * a valid ContentPanel config object.  Example:
32625      * <pre><code>
32626 // Create the main layout
32627 var layout = new Roo.BorderLayout('main-ct', {
32628     west: {
32629         split:true,
32630         minSize: 175,
32631         titlebar: true
32632     },
32633     center: {
32634         title:'Components'
32635     }
32636 }, 'main-ct');
32637
32638 // Create and add multiple ContentPanels at once via configs
32639 layout.batchAdd({
32640    west: {
32641        id: 'source-files',
32642        autoCreate:true,
32643        title:'Ext Source Files',
32644        autoScroll:true,
32645        fitToFrame:true
32646    },
32647    center : {
32648        el: cview,
32649        autoScroll:true,
32650        fitToFrame:true,
32651        toolbar: tb,
32652        resizeEl:'cbody'
32653    }
32654 });
32655 </code></pre>
32656      * @param {Object} regions An object containing ContentPanel configs by region name
32657      */
32658     batchAdd : function(regions){
32659         this.beginUpdate();
32660         for(var rname in regions){
32661             var lr = this.regions[rname];
32662             if(lr){
32663                 this.addTypedPanels(lr, regions[rname]);
32664             }
32665         }
32666         this.endUpdate();
32667     },
32668
32669     // private
32670     addTypedPanels : function(lr, ps){
32671         if(typeof ps == 'string'){
32672             lr.add(new Roo.ContentPanel(ps));
32673         }
32674         else if(ps instanceof Array){
32675             for(var i =0, len = ps.length; i < len; i++){
32676                 this.addTypedPanels(lr, ps[i]);
32677             }
32678         }
32679         else if(!ps.events){ // raw config?
32680             var el = ps.el;
32681             delete ps.el; // prevent conflict
32682             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32683         }
32684         else {  // panel object assumed!
32685             lr.add(ps);
32686         }
32687     },
32688     /**
32689      * Adds a xtype elements to the layout.
32690      * <pre><code>
32691
32692 layout.addxtype({
32693        xtype : 'ContentPanel',
32694        region: 'west',
32695        items: [ .... ]
32696    }
32697 );
32698
32699 layout.addxtype({
32700         xtype : 'NestedLayoutPanel',
32701         region: 'west',
32702         layout: {
32703            center: { },
32704            west: { }   
32705         },
32706         items : [ ... list of content panels or nested layout panels.. ]
32707    }
32708 );
32709 </code></pre>
32710      * @param {Object} cfg Xtype definition of item to add.
32711      */
32712     addxtype : function(cfg)
32713     {
32714         // basically accepts a pannel...
32715         // can accept a layout region..!?!?
32716         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32717         
32718         if (!cfg.xtype.match(/Panel$/)) {
32719             return false;
32720         }
32721         var ret = false;
32722         
32723         if (typeof(cfg.region) == 'undefined') {
32724             Roo.log("Failed to add Panel, region was not set");
32725             Roo.log(cfg);
32726             return false;
32727         }
32728         var region = cfg.region;
32729         delete cfg.region;
32730         
32731           
32732         var xitems = [];
32733         if (cfg.items) {
32734             xitems = cfg.items;
32735             delete cfg.items;
32736         }
32737         var nb = false;
32738         
32739         switch(cfg.xtype) 
32740         {
32741             case 'ContentPanel':  // ContentPanel (el, cfg)
32742             case 'ScrollPanel':  // ContentPanel (el, cfg)
32743             case 'ViewPanel': 
32744                 if(cfg.autoCreate) {
32745                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32746                 } else {
32747                     var el = this.el.createChild();
32748                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32749                 }
32750                 
32751                 this.add(region, ret);
32752                 break;
32753             
32754             
32755             case 'TreePanel': // our new panel!
32756                 cfg.el = this.el.createChild();
32757                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32758                 this.add(region, ret);
32759                 break;
32760             
32761             case 'NestedLayoutPanel': 
32762                 // create a new Layout (which is  a Border Layout...
32763                 var el = this.el.createChild();
32764                 var clayout = cfg.layout;
32765                 delete cfg.layout;
32766                 clayout.items   = clayout.items  || [];
32767                 // replace this exitems with the clayout ones..
32768                 xitems = clayout.items;
32769                  
32770                 
32771                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32772                     cfg.background = false;
32773                 }
32774                 var layout = new Roo.BorderLayout(el, clayout);
32775                 
32776                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32777                 //console.log('adding nested layout panel '  + cfg.toSource());
32778                 this.add(region, ret);
32779                 nb = {}; /// find first...
32780                 break;
32781                 
32782             case 'GridPanel': 
32783             
32784                 // needs grid and region
32785                 
32786                 //var el = this.getRegion(region).el.createChild();
32787                 var el = this.el.createChild();
32788                 // create the grid first...
32789                 
32790                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32791                 delete cfg.grid;
32792                 if (region == 'center' && this.active ) {
32793                     cfg.background = false;
32794                 }
32795                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32796                 
32797                 this.add(region, ret);
32798                 if (cfg.background) {
32799                     ret.on('activate', function(gp) {
32800                         if (!gp.grid.rendered) {
32801                             gp.grid.render();
32802                         }
32803                     });
32804                 } else {
32805                     grid.render();
32806                 }
32807                 break;
32808            
32809            
32810            
32811                 
32812                 
32813                 
32814             default: 
32815                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32816                 return null;
32817              // GridPanel (grid, cfg)
32818             
32819         }
32820         this.beginUpdate();
32821         // add children..
32822         var region = '';
32823         var abn = {};
32824         Roo.each(xitems, function(i)  {
32825             region = nb && i.region ? i.region : false;
32826             
32827             var add = ret.addxtype(i);
32828            
32829             if (region) {
32830                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32831                 if (!i.background) {
32832                     abn[region] = nb[region] ;
32833                 }
32834             }
32835             
32836         });
32837         this.endUpdate();
32838
32839         // make the last non-background panel active..
32840         //if (nb) { Roo.log(abn); }
32841         if (nb) {
32842             
32843             for(var r in abn) {
32844                 region = this.getRegion(r);
32845                 if (region) {
32846                     // tried using nb[r], but it does not work..
32847                      
32848                     region.showPanel(abn[r]);
32849                    
32850                 }
32851             }
32852         }
32853         return ret;
32854         
32855     }
32856 });
32857
32858 /**
32859  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32860  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32861  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32862  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32863  * <pre><code>
32864 // shorthand
32865 var CP = Roo.ContentPanel;
32866
32867 var layout = Roo.BorderLayout.create({
32868     north: {
32869         initialSize: 25,
32870         titlebar: false,
32871         panels: [new CP("north", "North")]
32872     },
32873     west: {
32874         split:true,
32875         initialSize: 200,
32876         minSize: 175,
32877         maxSize: 400,
32878         titlebar: true,
32879         collapsible: true,
32880         panels: [new CP("west", {title: "West"})]
32881     },
32882     east: {
32883         split:true,
32884         initialSize: 202,
32885         minSize: 175,
32886         maxSize: 400,
32887         titlebar: true,
32888         collapsible: true,
32889         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32890     },
32891     south: {
32892         split:true,
32893         initialSize: 100,
32894         minSize: 100,
32895         maxSize: 200,
32896         titlebar: true,
32897         collapsible: true,
32898         panels: [new CP("south", {title: "South", closable: true})]
32899     },
32900     center: {
32901         titlebar: true,
32902         autoScroll:true,
32903         resizeTabs: true,
32904         minTabWidth: 50,
32905         preferredTabWidth: 150,
32906         panels: [
32907             new CP("center1", {title: "Close Me", closable: true}),
32908             new CP("center2", {title: "Center Panel", closable: false})
32909         ]
32910     }
32911 }, document.body);
32912
32913 layout.getRegion("center").showPanel("center1");
32914 </code></pre>
32915  * @param config
32916  * @param targetEl
32917  */
32918 Roo.BorderLayout.create = function(config, targetEl){
32919     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32920     layout.beginUpdate();
32921     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32922     for(var j = 0, jlen = regions.length; j < jlen; j++){
32923         var lr = regions[j];
32924         if(layout.regions[lr] && config[lr].panels){
32925             var r = layout.regions[lr];
32926             var ps = config[lr].panels;
32927             layout.addTypedPanels(r, ps);
32928         }
32929     }
32930     layout.endUpdate();
32931     return layout;
32932 };
32933
32934 // private
32935 Roo.BorderLayout.RegionFactory = {
32936     // private
32937     validRegions : ["north","south","east","west","center"],
32938
32939     // private
32940     create : function(target, mgr, config){
32941         target = target.toLowerCase();
32942         if(config.lightweight || config.basic){
32943             return new Roo.BasicLayoutRegion(mgr, config, target);
32944         }
32945         switch(target){
32946             case "north":
32947                 return new Roo.NorthLayoutRegion(mgr, config);
32948             case "south":
32949                 return new Roo.SouthLayoutRegion(mgr, config);
32950             case "east":
32951                 return new Roo.EastLayoutRegion(mgr, config);
32952             case "west":
32953                 return new Roo.WestLayoutRegion(mgr, config);
32954             case "center":
32955                 return new Roo.CenterLayoutRegion(mgr, config);
32956         }
32957         throw 'Layout region "'+target+'" not supported.';
32958     }
32959 };/*
32960  * Based on:
32961  * Ext JS Library 1.1.1
32962  * Copyright(c) 2006-2007, Ext JS, LLC.
32963  *
32964  * Originally Released Under LGPL - original licence link has changed is not relivant.
32965  *
32966  * Fork - LGPL
32967  * <script type="text/javascript">
32968  */
32969  
32970 /**
32971  * @class Roo.BasicLayoutRegion
32972  * @extends Roo.util.Observable
32973  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32974  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32975  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32976  */
32977 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32978     this.mgr = mgr;
32979     this.position  = pos;
32980     this.events = {
32981         /**
32982          * @scope Roo.BasicLayoutRegion
32983          */
32984         
32985         /**
32986          * @event beforeremove
32987          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32988          * @param {Roo.LayoutRegion} this
32989          * @param {Roo.ContentPanel} panel The panel
32990          * @param {Object} e The cancel event object
32991          */
32992         "beforeremove" : true,
32993         /**
32994          * @event invalidated
32995          * Fires when the layout for this region is changed.
32996          * @param {Roo.LayoutRegion} this
32997          */
32998         "invalidated" : true,
32999         /**
33000          * @event visibilitychange
33001          * Fires when this region is shown or hidden 
33002          * @param {Roo.LayoutRegion} this
33003          * @param {Boolean} visibility true or false
33004          */
33005         "visibilitychange" : true,
33006         /**
33007          * @event paneladded
33008          * Fires when a panel is added. 
33009          * @param {Roo.LayoutRegion} this
33010          * @param {Roo.ContentPanel} panel The panel
33011          */
33012         "paneladded" : true,
33013         /**
33014          * @event panelremoved
33015          * Fires when a panel is removed. 
33016          * @param {Roo.LayoutRegion} this
33017          * @param {Roo.ContentPanel} panel The panel
33018          */
33019         "panelremoved" : true,
33020         /**
33021          * @event collapsed
33022          * Fires when this region is collapsed.
33023          * @param {Roo.LayoutRegion} this
33024          */
33025         "collapsed" : true,
33026         /**
33027          * @event expanded
33028          * Fires when this region is expanded.
33029          * @param {Roo.LayoutRegion} this
33030          */
33031         "expanded" : true,
33032         /**
33033          * @event slideshow
33034          * Fires when this region is slid into view.
33035          * @param {Roo.LayoutRegion} this
33036          */
33037         "slideshow" : true,
33038         /**
33039          * @event slidehide
33040          * Fires when this region slides out of view. 
33041          * @param {Roo.LayoutRegion} this
33042          */
33043         "slidehide" : true,
33044         /**
33045          * @event panelactivated
33046          * Fires when a panel is activated. 
33047          * @param {Roo.LayoutRegion} this
33048          * @param {Roo.ContentPanel} panel The activated panel
33049          */
33050         "panelactivated" : true,
33051         /**
33052          * @event resized
33053          * Fires when the user resizes this region. 
33054          * @param {Roo.LayoutRegion} this
33055          * @param {Number} newSize The new size (width for east/west, height for north/south)
33056          */
33057         "resized" : true
33058     };
33059     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33060     this.panels = new Roo.util.MixedCollection();
33061     this.panels.getKey = this.getPanelId.createDelegate(this);
33062     this.box = null;
33063     this.activePanel = null;
33064     // ensure listeners are added...
33065     
33066     if (config.listeners || config.events) {
33067         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33068             listeners : config.listeners || {},
33069             events : config.events || {}
33070         });
33071     }
33072     
33073     if(skipConfig !== true){
33074         this.applyConfig(config);
33075     }
33076 };
33077
33078 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33079     getPanelId : function(p){
33080         return p.getId();
33081     },
33082     
33083     applyConfig : function(config){
33084         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33085         this.config = config;
33086         
33087     },
33088     
33089     /**
33090      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33091      * the width, for horizontal (north, south) the height.
33092      * @param {Number} newSize The new width or height
33093      */
33094     resizeTo : function(newSize){
33095         var el = this.el ? this.el :
33096                  (this.activePanel ? this.activePanel.getEl() : null);
33097         if(el){
33098             switch(this.position){
33099                 case "east":
33100                 case "west":
33101                     el.setWidth(newSize);
33102                     this.fireEvent("resized", this, newSize);
33103                 break;
33104                 case "north":
33105                 case "south":
33106                     el.setHeight(newSize);
33107                     this.fireEvent("resized", this, newSize);
33108                 break;                
33109             }
33110         }
33111     },
33112     
33113     getBox : function(){
33114         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33115     },
33116     
33117     getMargins : function(){
33118         return this.margins;
33119     },
33120     
33121     updateBox : function(box){
33122         this.box = box;
33123         var el = this.activePanel.getEl();
33124         el.dom.style.left = box.x + "px";
33125         el.dom.style.top = box.y + "px";
33126         this.activePanel.setSize(box.width, box.height);
33127     },
33128     
33129     /**
33130      * Returns the container element for this region.
33131      * @return {Roo.Element}
33132      */
33133     getEl : function(){
33134         return this.activePanel;
33135     },
33136     
33137     /**
33138      * Returns true if this region is currently visible.
33139      * @return {Boolean}
33140      */
33141     isVisible : function(){
33142         return this.activePanel ? true : false;
33143     },
33144     
33145     setActivePanel : function(panel){
33146         panel = this.getPanel(panel);
33147         if(this.activePanel && this.activePanel != panel){
33148             this.activePanel.setActiveState(false);
33149             this.activePanel.getEl().setLeftTop(-10000,-10000);
33150         }
33151         this.activePanel = panel;
33152         panel.setActiveState(true);
33153         if(this.box){
33154             panel.setSize(this.box.width, this.box.height);
33155         }
33156         this.fireEvent("panelactivated", this, panel);
33157         this.fireEvent("invalidated");
33158     },
33159     
33160     /**
33161      * Show the specified panel.
33162      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33163      * @return {Roo.ContentPanel} The shown panel or null
33164      */
33165     showPanel : function(panel){
33166         if(panel = this.getPanel(panel)){
33167             this.setActivePanel(panel);
33168         }
33169         return panel;
33170     },
33171     
33172     /**
33173      * Get the active panel for this region.
33174      * @return {Roo.ContentPanel} The active panel or null
33175      */
33176     getActivePanel : function(){
33177         return this.activePanel;
33178     },
33179     
33180     /**
33181      * Add the passed ContentPanel(s)
33182      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33183      * @return {Roo.ContentPanel} The panel added (if only one was added)
33184      */
33185     add : function(panel){
33186         if(arguments.length > 1){
33187             for(var i = 0, len = arguments.length; i < len; i++) {
33188                 this.add(arguments[i]);
33189             }
33190             return null;
33191         }
33192         if(this.hasPanel(panel)){
33193             this.showPanel(panel);
33194             return panel;
33195         }
33196         var el = panel.getEl();
33197         if(el.dom.parentNode != this.mgr.el.dom){
33198             this.mgr.el.dom.appendChild(el.dom);
33199         }
33200         if(panel.setRegion){
33201             panel.setRegion(this);
33202         }
33203         this.panels.add(panel);
33204         el.setStyle("position", "absolute");
33205         if(!panel.background){
33206             this.setActivePanel(panel);
33207             if(this.config.initialSize && this.panels.getCount()==1){
33208                 this.resizeTo(this.config.initialSize);
33209             }
33210         }
33211         this.fireEvent("paneladded", this, panel);
33212         return panel;
33213     },
33214     
33215     /**
33216      * Returns true if the panel is in this region.
33217      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33218      * @return {Boolean}
33219      */
33220     hasPanel : function(panel){
33221         if(typeof panel == "object"){ // must be panel obj
33222             panel = panel.getId();
33223         }
33224         return this.getPanel(panel) ? true : false;
33225     },
33226     
33227     /**
33228      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33229      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33230      * @param {Boolean} preservePanel Overrides the config preservePanel option
33231      * @return {Roo.ContentPanel} The panel that was removed
33232      */
33233     remove : function(panel, preservePanel){
33234         panel = this.getPanel(panel);
33235         if(!panel){
33236             return null;
33237         }
33238         var e = {};
33239         this.fireEvent("beforeremove", this, panel, e);
33240         if(e.cancel === true){
33241             return null;
33242         }
33243         var panelId = panel.getId();
33244         this.panels.removeKey(panelId);
33245         return panel;
33246     },
33247     
33248     /**
33249      * Returns the panel specified or null if it's not in this region.
33250      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33251      * @return {Roo.ContentPanel}
33252      */
33253     getPanel : function(id){
33254         if(typeof id == "object"){ // must be panel obj
33255             return id;
33256         }
33257         return this.panels.get(id);
33258     },
33259     
33260     /**
33261      * Returns this regions position (north/south/east/west/center).
33262      * @return {String} 
33263      */
33264     getPosition: function(){
33265         return this.position;    
33266     }
33267 });/*
33268  * Based on:
33269  * Ext JS Library 1.1.1
33270  * Copyright(c) 2006-2007, Ext JS, LLC.
33271  *
33272  * Originally Released Under LGPL - original licence link has changed is not relivant.
33273  *
33274  * Fork - LGPL
33275  * <script type="text/javascript">
33276  */
33277  
33278 /**
33279  * @class Roo.LayoutRegion
33280  * @extends Roo.BasicLayoutRegion
33281  * This class represents a region in a layout manager.
33282  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33283  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33284  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33285  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33286  * @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})
33287  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33288  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33289  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33290  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33291  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33292  * @cfg {String}    title           The title for the region (overrides panel titles)
33293  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33294  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33295  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33296  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33297  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33298  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33299  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33300  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33301  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33302  * @cfg {Boolean}   showPin         True to show a pin button
33303  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33304  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33305  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33306  * @cfg {Number}    width           For East/West panels
33307  * @cfg {Number}    height          For North/South panels
33308  * @cfg {Boolean}   split           To show the splitter
33309  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33310  */
33311 Roo.LayoutRegion = function(mgr, config, pos){
33312     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33313     var dh = Roo.DomHelper;
33314     /** This region's container element 
33315     * @type Roo.Element */
33316     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33317     /** This region's title element 
33318     * @type Roo.Element */
33319
33320     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33321         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33322         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33323     ]}, true);
33324     this.titleEl.enableDisplayMode();
33325     /** This region's title text element 
33326     * @type HTMLElement */
33327     this.titleTextEl = this.titleEl.dom.firstChild;
33328     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33329     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33330     this.closeBtn.enableDisplayMode();
33331     this.closeBtn.on("click", this.closeClicked, this);
33332     this.closeBtn.hide();
33333
33334     this.createBody(config);
33335     this.visible = true;
33336     this.collapsed = false;
33337
33338     if(config.hideWhenEmpty){
33339         this.hide();
33340         this.on("paneladded", this.validateVisibility, this);
33341         this.on("panelremoved", this.validateVisibility, this);
33342     }
33343     this.applyConfig(config);
33344 };
33345
33346 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33347
33348     createBody : function(){
33349         /** This region's body element 
33350         * @type Roo.Element */
33351         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33352     },
33353
33354     applyConfig : function(c){
33355         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33356             var dh = Roo.DomHelper;
33357             if(c.titlebar !== false){
33358                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33359                 this.collapseBtn.on("click", this.collapse, this);
33360                 this.collapseBtn.enableDisplayMode();
33361
33362                 if(c.showPin === true || this.showPin){
33363                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33364                     this.stickBtn.enableDisplayMode();
33365                     this.stickBtn.on("click", this.expand, this);
33366                     this.stickBtn.hide();
33367                 }
33368             }
33369             /** This region's collapsed element
33370             * @type Roo.Element */
33371             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33372                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33373             ]}, true);
33374             if(c.floatable !== false){
33375                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33376                this.collapsedEl.on("click", this.collapseClick, this);
33377             }
33378
33379             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33380                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33381                    id: "message", unselectable: "on", style:{"float":"left"}});
33382                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33383              }
33384             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33385             this.expandBtn.on("click", this.expand, this);
33386         }
33387         if(this.collapseBtn){
33388             this.collapseBtn.setVisible(c.collapsible == true);
33389         }
33390         this.cmargins = c.cmargins || this.cmargins ||
33391                          (this.position == "west" || this.position == "east" ?
33392                              {top: 0, left: 2, right:2, bottom: 0} :
33393                              {top: 2, left: 0, right:0, bottom: 2});
33394         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33395         this.bottomTabs = c.tabPosition != "top";
33396         this.autoScroll = c.autoScroll || false;
33397         if(this.autoScroll){
33398             this.bodyEl.setStyle("overflow", "auto");
33399         }else{
33400             this.bodyEl.setStyle("overflow", "hidden");
33401         }
33402         //if(c.titlebar !== false){
33403             if((!c.titlebar && !c.title) || c.titlebar === false){
33404                 this.titleEl.hide();
33405             }else{
33406                 this.titleEl.show();
33407                 if(c.title){
33408                     this.titleTextEl.innerHTML = c.title;
33409                 }
33410             }
33411         //}
33412         this.duration = c.duration || .30;
33413         this.slideDuration = c.slideDuration || .45;
33414         this.config = c;
33415         if(c.collapsed){
33416             this.collapse(true);
33417         }
33418         if(c.hidden){
33419             this.hide();
33420         }
33421     },
33422     /**
33423      * Returns true if this region is currently visible.
33424      * @return {Boolean}
33425      */
33426     isVisible : function(){
33427         return this.visible;
33428     },
33429
33430     /**
33431      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33432      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33433      */
33434     setCollapsedTitle : function(title){
33435         title = title || "&#160;";
33436         if(this.collapsedTitleTextEl){
33437             this.collapsedTitleTextEl.innerHTML = title;
33438         }
33439     },
33440
33441     getBox : function(){
33442         var b;
33443         if(!this.collapsed){
33444             b = this.el.getBox(false, true);
33445         }else{
33446             b = this.collapsedEl.getBox(false, true);
33447         }
33448         return b;
33449     },
33450
33451     getMargins : function(){
33452         return this.collapsed ? this.cmargins : this.margins;
33453     },
33454
33455     highlight : function(){
33456         this.el.addClass("x-layout-panel-dragover");
33457     },
33458
33459     unhighlight : function(){
33460         this.el.removeClass("x-layout-panel-dragover");
33461     },
33462
33463     updateBox : function(box){
33464         this.box = box;
33465         if(!this.collapsed){
33466             this.el.dom.style.left = box.x + "px";
33467             this.el.dom.style.top = box.y + "px";
33468             this.updateBody(box.width, box.height);
33469         }else{
33470             this.collapsedEl.dom.style.left = box.x + "px";
33471             this.collapsedEl.dom.style.top = box.y + "px";
33472             this.collapsedEl.setSize(box.width, box.height);
33473         }
33474         if(this.tabs){
33475             this.tabs.autoSizeTabs();
33476         }
33477     },
33478
33479     updateBody : function(w, h){
33480         if(w !== null){
33481             this.el.setWidth(w);
33482             w -= this.el.getBorderWidth("rl");
33483             if(this.config.adjustments){
33484                 w += this.config.adjustments[0];
33485             }
33486         }
33487         if(h !== null){
33488             this.el.setHeight(h);
33489             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33490             h -= this.el.getBorderWidth("tb");
33491             if(this.config.adjustments){
33492                 h += this.config.adjustments[1];
33493             }
33494             this.bodyEl.setHeight(h);
33495             if(this.tabs){
33496                 h = this.tabs.syncHeight(h);
33497             }
33498         }
33499         if(this.panelSize){
33500             w = w !== null ? w : this.panelSize.width;
33501             h = h !== null ? h : this.panelSize.height;
33502         }
33503         if(this.activePanel){
33504             var el = this.activePanel.getEl();
33505             w = w !== null ? w : el.getWidth();
33506             h = h !== null ? h : el.getHeight();
33507             this.panelSize = {width: w, height: h};
33508             this.activePanel.setSize(w, h);
33509         }
33510         if(Roo.isIE && this.tabs){
33511             this.tabs.el.repaint();
33512         }
33513     },
33514
33515     /**
33516      * Returns the container element for this region.
33517      * @return {Roo.Element}
33518      */
33519     getEl : function(){
33520         return this.el;
33521     },
33522
33523     /**
33524      * Hides this region.
33525      */
33526     hide : function(){
33527         if(!this.collapsed){
33528             this.el.dom.style.left = "-2000px";
33529             this.el.hide();
33530         }else{
33531             this.collapsedEl.dom.style.left = "-2000px";
33532             this.collapsedEl.hide();
33533         }
33534         this.visible = false;
33535         this.fireEvent("visibilitychange", this, false);
33536     },
33537
33538     /**
33539      * Shows this region if it was previously hidden.
33540      */
33541     show : function(){
33542         if(!this.collapsed){
33543             this.el.show();
33544         }else{
33545             this.collapsedEl.show();
33546         }
33547         this.visible = true;
33548         this.fireEvent("visibilitychange", this, true);
33549     },
33550
33551     closeClicked : function(){
33552         if(this.activePanel){
33553             this.remove(this.activePanel);
33554         }
33555     },
33556
33557     collapseClick : function(e){
33558         if(this.isSlid){
33559            e.stopPropagation();
33560            this.slideIn();
33561         }else{
33562            e.stopPropagation();
33563            this.slideOut();
33564         }
33565     },
33566
33567     /**
33568      * Collapses this region.
33569      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33570      */
33571     collapse : function(skipAnim){
33572         if(this.collapsed) return;
33573         this.collapsed = true;
33574         if(this.split){
33575             this.split.el.hide();
33576         }
33577         if(this.config.animate && skipAnim !== true){
33578             this.fireEvent("invalidated", this);
33579             this.animateCollapse();
33580         }else{
33581             this.el.setLocation(-20000,-20000);
33582             this.el.hide();
33583             this.collapsedEl.show();
33584             this.fireEvent("collapsed", this);
33585             this.fireEvent("invalidated", this);
33586         }
33587     },
33588
33589     animateCollapse : function(){
33590         // overridden
33591     },
33592
33593     /**
33594      * Expands this region if it was previously collapsed.
33595      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33596      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33597      */
33598     expand : function(e, skipAnim){
33599         if(e) e.stopPropagation();
33600         if(!this.collapsed || this.el.hasActiveFx()) return;
33601         if(this.isSlid){
33602             this.afterSlideIn();
33603             skipAnim = true;
33604         }
33605         this.collapsed = false;
33606         if(this.config.animate && skipAnim !== true){
33607             this.animateExpand();
33608         }else{
33609             this.el.show();
33610             if(this.split){
33611                 this.split.el.show();
33612             }
33613             this.collapsedEl.setLocation(-2000,-2000);
33614             this.collapsedEl.hide();
33615             this.fireEvent("invalidated", this);
33616             this.fireEvent("expanded", this);
33617         }
33618     },
33619
33620     animateExpand : function(){
33621         // overridden
33622     },
33623
33624     initTabs : function()
33625     {
33626         this.bodyEl.setStyle("overflow", "hidden");
33627         var ts = new Roo.TabPanel(
33628                 this.bodyEl.dom,
33629                 {
33630                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33631                     disableTooltips: this.config.disableTabTips,
33632                     toolbar : this.config.toolbar
33633                 }
33634         );
33635         if(this.config.hideTabs){
33636             ts.stripWrap.setDisplayed(false);
33637         }
33638         this.tabs = ts;
33639         ts.resizeTabs = this.config.resizeTabs === true;
33640         ts.minTabWidth = this.config.minTabWidth || 40;
33641         ts.maxTabWidth = this.config.maxTabWidth || 250;
33642         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33643         ts.monitorResize = false;
33644         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33645         ts.bodyEl.addClass('x-layout-tabs-body');
33646         this.panels.each(this.initPanelAsTab, this);
33647     },
33648
33649     initPanelAsTab : function(panel){
33650         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33651                     this.config.closeOnTab && panel.isClosable());
33652         if(panel.tabTip !== undefined){
33653             ti.setTooltip(panel.tabTip);
33654         }
33655         ti.on("activate", function(){
33656               this.setActivePanel(panel);
33657         }, this);
33658         if(this.config.closeOnTab){
33659             ti.on("beforeclose", function(t, e){
33660                 e.cancel = true;
33661                 this.remove(panel);
33662             }, this);
33663         }
33664         return ti;
33665     },
33666
33667     updatePanelTitle : function(panel, title){
33668         if(this.activePanel == panel){
33669             this.updateTitle(title);
33670         }
33671         if(this.tabs){
33672             var ti = this.tabs.getTab(panel.getEl().id);
33673             ti.setText(title);
33674             if(panel.tabTip !== undefined){
33675                 ti.setTooltip(panel.tabTip);
33676             }
33677         }
33678     },
33679
33680     updateTitle : function(title){
33681         if(this.titleTextEl && !this.config.title){
33682             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33683         }
33684     },
33685
33686     setActivePanel : function(panel){
33687         panel = this.getPanel(panel);
33688         if(this.activePanel && this.activePanel != panel){
33689             this.activePanel.setActiveState(false);
33690         }
33691         this.activePanel = panel;
33692         panel.setActiveState(true);
33693         if(this.panelSize){
33694             panel.setSize(this.panelSize.width, this.panelSize.height);
33695         }
33696         if(this.closeBtn){
33697             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33698         }
33699         this.updateTitle(panel.getTitle());
33700         if(this.tabs){
33701             this.fireEvent("invalidated", this);
33702         }
33703         this.fireEvent("panelactivated", this, panel);
33704     },
33705
33706     /**
33707      * Shows the specified panel.
33708      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33709      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33710      */
33711     showPanel : function(panel){
33712         if(panel = this.getPanel(panel)){
33713             if(this.tabs){
33714                 var tab = this.tabs.getTab(panel.getEl().id);
33715                 if(tab.isHidden()){
33716                     this.tabs.unhideTab(tab.id);
33717                 }
33718                 tab.activate();
33719             }else{
33720                 this.setActivePanel(panel);
33721             }
33722         }
33723         return panel;
33724     },
33725
33726     /**
33727      * Get the active panel for this region.
33728      * @return {Roo.ContentPanel} The active panel or null
33729      */
33730     getActivePanel : function(){
33731         return this.activePanel;
33732     },
33733
33734     validateVisibility : function(){
33735         if(this.panels.getCount() < 1){
33736             this.updateTitle("&#160;");
33737             this.closeBtn.hide();
33738             this.hide();
33739         }else{
33740             if(!this.isVisible()){
33741                 this.show();
33742             }
33743         }
33744     },
33745
33746     /**
33747      * Adds the passed ContentPanel(s) to this region.
33748      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33749      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33750      */
33751     add : function(panel){
33752         if(arguments.length > 1){
33753             for(var i = 0, len = arguments.length; i < len; i++) {
33754                 this.add(arguments[i]);
33755             }
33756             return null;
33757         }
33758         if(this.hasPanel(panel)){
33759             this.showPanel(panel);
33760             return panel;
33761         }
33762         panel.setRegion(this);
33763         this.panels.add(panel);
33764         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33765             this.bodyEl.dom.appendChild(panel.getEl().dom);
33766             if(panel.background !== true){
33767                 this.setActivePanel(panel);
33768             }
33769             this.fireEvent("paneladded", this, panel);
33770             return panel;
33771         }
33772         if(!this.tabs){
33773             this.initTabs();
33774         }else{
33775             this.initPanelAsTab(panel);
33776         }
33777         if(panel.background !== true){
33778             this.tabs.activate(panel.getEl().id);
33779         }
33780         this.fireEvent("paneladded", this, panel);
33781         return panel;
33782     },
33783
33784     /**
33785      * Hides the tab for the specified panel.
33786      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33787      */
33788     hidePanel : function(panel){
33789         if(this.tabs && (panel = this.getPanel(panel))){
33790             this.tabs.hideTab(panel.getEl().id);
33791         }
33792     },
33793
33794     /**
33795      * Unhides the tab for a previously hidden panel.
33796      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33797      */
33798     unhidePanel : function(panel){
33799         if(this.tabs && (panel = this.getPanel(panel))){
33800             this.tabs.unhideTab(panel.getEl().id);
33801         }
33802     },
33803
33804     clearPanels : function(){
33805         while(this.panels.getCount() > 0){
33806              this.remove(this.panels.first());
33807         }
33808     },
33809
33810     /**
33811      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33812      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33813      * @param {Boolean} preservePanel Overrides the config preservePanel option
33814      * @return {Roo.ContentPanel} The panel that was removed
33815      */
33816     remove : function(panel, preservePanel){
33817         panel = this.getPanel(panel);
33818         if(!panel){
33819             return null;
33820         }
33821         var e = {};
33822         this.fireEvent("beforeremove", this, panel, e);
33823         if(e.cancel === true){
33824             return null;
33825         }
33826         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33827         var panelId = panel.getId();
33828         this.panels.removeKey(panelId);
33829         if(preservePanel){
33830             document.body.appendChild(panel.getEl().dom);
33831         }
33832         if(this.tabs){
33833             this.tabs.removeTab(panel.getEl().id);
33834         }else if (!preservePanel){
33835             this.bodyEl.dom.removeChild(panel.getEl().dom);
33836         }
33837         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33838             var p = this.panels.first();
33839             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33840             tempEl.appendChild(p.getEl().dom);
33841             this.bodyEl.update("");
33842             this.bodyEl.dom.appendChild(p.getEl().dom);
33843             tempEl = null;
33844             this.updateTitle(p.getTitle());
33845             this.tabs = null;
33846             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33847             this.setActivePanel(p);
33848         }
33849         panel.setRegion(null);
33850         if(this.activePanel == panel){
33851             this.activePanel = null;
33852         }
33853         if(this.config.autoDestroy !== false && preservePanel !== true){
33854             try{panel.destroy();}catch(e){}
33855         }
33856         this.fireEvent("panelremoved", this, panel);
33857         return panel;
33858     },
33859
33860     /**
33861      * Returns the TabPanel component used by this region
33862      * @return {Roo.TabPanel}
33863      */
33864     getTabs : function(){
33865         return this.tabs;
33866     },
33867
33868     createTool : function(parentEl, className){
33869         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33870             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33871         btn.addClassOnOver("x-layout-tools-button-over");
33872         return btn;
33873     }
33874 });/*
33875  * Based on:
33876  * Ext JS Library 1.1.1
33877  * Copyright(c) 2006-2007, Ext JS, LLC.
33878  *
33879  * Originally Released Under LGPL - original licence link has changed is not relivant.
33880  *
33881  * Fork - LGPL
33882  * <script type="text/javascript">
33883  */
33884  
33885
33886
33887 /**
33888  * @class Roo.SplitLayoutRegion
33889  * @extends Roo.LayoutRegion
33890  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33891  */
33892 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33893     this.cursor = cursor;
33894     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33895 };
33896
33897 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33898     splitTip : "Drag to resize.",
33899     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33900     useSplitTips : false,
33901
33902     applyConfig : function(config){
33903         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33904         if(config.split){
33905             if(!this.split){
33906                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33907                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33908                 /** The SplitBar for this region 
33909                 * @type Roo.SplitBar */
33910                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33911                 this.split.on("moved", this.onSplitMove, this);
33912                 this.split.useShim = config.useShim === true;
33913                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33914                 if(this.useSplitTips){
33915                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33916                 }
33917                 if(config.collapsible){
33918                     this.split.el.on("dblclick", this.collapse,  this);
33919                 }
33920             }
33921             if(typeof config.minSize != "undefined"){
33922                 this.split.minSize = config.minSize;
33923             }
33924             if(typeof config.maxSize != "undefined"){
33925                 this.split.maxSize = config.maxSize;
33926             }
33927             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33928                 this.hideSplitter();
33929             }
33930         }
33931     },
33932
33933     getHMaxSize : function(){
33934          var cmax = this.config.maxSize || 10000;
33935          var center = this.mgr.getRegion("center");
33936          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33937     },
33938
33939     getVMaxSize : function(){
33940          var cmax = this.config.maxSize || 10000;
33941          var center = this.mgr.getRegion("center");
33942          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33943     },
33944
33945     onSplitMove : function(split, newSize){
33946         this.fireEvent("resized", this, newSize);
33947     },
33948     
33949     /** 
33950      * Returns the {@link Roo.SplitBar} for this region.
33951      * @return {Roo.SplitBar}
33952      */
33953     getSplitBar : function(){
33954         return this.split;
33955     },
33956     
33957     hide : function(){
33958         this.hideSplitter();
33959         Roo.SplitLayoutRegion.superclass.hide.call(this);
33960     },
33961
33962     hideSplitter : function(){
33963         if(this.split){
33964             this.split.el.setLocation(-2000,-2000);
33965             this.split.el.hide();
33966         }
33967     },
33968
33969     show : function(){
33970         if(this.split){
33971             this.split.el.show();
33972         }
33973         Roo.SplitLayoutRegion.superclass.show.call(this);
33974     },
33975     
33976     beforeSlide: function(){
33977         if(Roo.isGecko){// firefox overflow auto bug workaround
33978             this.bodyEl.clip();
33979             if(this.tabs) this.tabs.bodyEl.clip();
33980             if(this.activePanel){
33981                 this.activePanel.getEl().clip();
33982                 
33983                 if(this.activePanel.beforeSlide){
33984                     this.activePanel.beforeSlide();
33985                 }
33986             }
33987         }
33988     },
33989     
33990     afterSlide : function(){
33991         if(Roo.isGecko){// firefox overflow auto bug workaround
33992             this.bodyEl.unclip();
33993             if(this.tabs) this.tabs.bodyEl.unclip();
33994             if(this.activePanel){
33995                 this.activePanel.getEl().unclip();
33996                 if(this.activePanel.afterSlide){
33997                     this.activePanel.afterSlide();
33998                 }
33999             }
34000         }
34001     },
34002
34003     initAutoHide : function(){
34004         if(this.autoHide !== false){
34005             if(!this.autoHideHd){
34006                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34007                 this.autoHideHd = {
34008                     "mouseout": function(e){
34009                         if(!e.within(this.el, true)){
34010                             st.delay(500);
34011                         }
34012                     },
34013                     "mouseover" : function(e){
34014                         st.cancel();
34015                     },
34016                     scope : this
34017                 };
34018             }
34019             this.el.on(this.autoHideHd);
34020         }
34021     },
34022
34023     clearAutoHide : function(){
34024         if(this.autoHide !== false){
34025             this.el.un("mouseout", this.autoHideHd.mouseout);
34026             this.el.un("mouseover", this.autoHideHd.mouseover);
34027         }
34028     },
34029
34030     clearMonitor : function(){
34031         Roo.get(document).un("click", this.slideInIf, this);
34032     },
34033
34034     // these names are backwards but not changed for compat
34035     slideOut : function(){
34036         if(this.isSlid || this.el.hasActiveFx()){
34037             return;
34038         }
34039         this.isSlid = true;
34040         if(this.collapseBtn){
34041             this.collapseBtn.hide();
34042         }
34043         this.closeBtnState = this.closeBtn.getStyle('display');
34044         this.closeBtn.hide();
34045         if(this.stickBtn){
34046             this.stickBtn.show();
34047         }
34048         this.el.show();
34049         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34050         this.beforeSlide();
34051         this.el.setStyle("z-index", 10001);
34052         this.el.slideIn(this.getSlideAnchor(), {
34053             callback: function(){
34054                 this.afterSlide();
34055                 this.initAutoHide();
34056                 Roo.get(document).on("click", this.slideInIf, this);
34057                 this.fireEvent("slideshow", this);
34058             },
34059             scope: this,
34060             block: true
34061         });
34062     },
34063
34064     afterSlideIn : function(){
34065         this.clearAutoHide();
34066         this.isSlid = false;
34067         this.clearMonitor();
34068         this.el.setStyle("z-index", "");
34069         if(this.collapseBtn){
34070             this.collapseBtn.show();
34071         }
34072         this.closeBtn.setStyle('display', this.closeBtnState);
34073         if(this.stickBtn){
34074             this.stickBtn.hide();
34075         }
34076         this.fireEvent("slidehide", this);
34077     },
34078
34079     slideIn : function(cb){
34080         if(!this.isSlid || this.el.hasActiveFx()){
34081             Roo.callback(cb);
34082             return;
34083         }
34084         this.isSlid = false;
34085         this.beforeSlide();
34086         this.el.slideOut(this.getSlideAnchor(), {
34087             callback: function(){
34088                 this.el.setLeftTop(-10000, -10000);
34089                 this.afterSlide();
34090                 this.afterSlideIn();
34091                 Roo.callback(cb);
34092             },
34093             scope: this,
34094             block: true
34095         });
34096     },
34097     
34098     slideInIf : function(e){
34099         if(!e.within(this.el)){
34100             this.slideIn();
34101         }
34102     },
34103
34104     animateCollapse : function(){
34105         this.beforeSlide();
34106         this.el.setStyle("z-index", 20000);
34107         var anchor = this.getSlideAnchor();
34108         this.el.slideOut(anchor, {
34109             callback : function(){
34110                 this.el.setStyle("z-index", "");
34111                 this.collapsedEl.slideIn(anchor, {duration:.3});
34112                 this.afterSlide();
34113                 this.el.setLocation(-10000,-10000);
34114                 this.el.hide();
34115                 this.fireEvent("collapsed", this);
34116             },
34117             scope: this,
34118             block: true
34119         });
34120     },
34121
34122     animateExpand : function(){
34123         this.beforeSlide();
34124         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34125         this.el.setStyle("z-index", 20000);
34126         this.collapsedEl.hide({
34127             duration:.1
34128         });
34129         this.el.slideIn(this.getSlideAnchor(), {
34130             callback : function(){
34131                 this.el.setStyle("z-index", "");
34132                 this.afterSlide();
34133                 if(this.split){
34134                     this.split.el.show();
34135                 }
34136                 this.fireEvent("invalidated", this);
34137                 this.fireEvent("expanded", this);
34138             },
34139             scope: this,
34140             block: true
34141         });
34142     },
34143
34144     anchors : {
34145         "west" : "left",
34146         "east" : "right",
34147         "north" : "top",
34148         "south" : "bottom"
34149     },
34150
34151     sanchors : {
34152         "west" : "l",
34153         "east" : "r",
34154         "north" : "t",
34155         "south" : "b"
34156     },
34157
34158     canchors : {
34159         "west" : "tl-tr",
34160         "east" : "tr-tl",
34161         "north" : "tl-bl",
34162         "south" : "bl-tl"
34163     },
34164
34165     getAnchor : function(){
34166         return this.anchors[this.position];
34167     },
34168
34169     getCollapseAnchor : function(){
34170         return this.canchors[this.position];
34171     },
34172
34173     getSlideAnchor : function(){
34174         return this.sanchors[this.position];
34175     },
34176
34177     getAlignAdj : function(){
34178         var cm = this.cmargins;
34179         switch(this.position){
34180             case "west":
34181                 return [0, 0];
34182             break;
34183             case "east":
34184                 return [0, 0];
34185             break;
34186             case "north":
34187                 return [0, 0];
34188             break;
34189             case "south":
34190                 return [0, 0];
34191             break;
34192         }
34193     },
34194
34195     getExpandAdj : function(){
34196         var c = this.collapsedEl, cm = this.cmargins;
34197         switch(this.position){
34198             case "west":
34199                 return [-(cm.right+c.getWidth()+cm.left), 0];
34200             break;
34201             case "east":
34202                 return [cm.right+c.getWidth()+cm.left, 0];
34203             break;
34204             case "north":
34205                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34206             break;
34207             case "south":
34208                 return [0, cm.top+cm.bottom+c.getHeight()];
34209             break;
34210         }
34211     }
34212 });/*
34213  * Based on:
34214  * Ext JS Library 1.1.1
34215  * Copyright(c) 2006-2007, Ext JS, LLC.
34216  *
34217  * Originally Released Under LGPL - original licence link has changed is not relivant.
34218  *
34219  * Fork - LGPL
34220  * <script type="text/javascript">
34221  */
34222 /*
34223  * These classes are private internal classes
34224  */
34225 Roo.CenterLayoutRegion = function(mgr, config){
34226     Roo.LayoutRegion.call(this, mgr, config, "center");
34227     this.visible = true;
34228     this.minWidth = config.minWidth || 20;
34229     this.minHeight = config.minHeight || 20;
34230 };
34231
34232 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34233     hide : function(){
34234         // center panel can't be hidden
34235     },
34236     
34237     show : function(){
34238         // center panel can't be hidden
34239     },
34240     
34241     getMinWidth: function(){
34242         return this.minWidth;
34243     },
34244     
34245     getMinHeight: function(){
34246         return this.minHeight;
34247     }
34248 });
34249
34250
34251 Roo.NorthLayoutRegion = function(mgr, config){
34252     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34253     if(this.split){
34254         this.split.placement = Roo.SplitBar.TOP;
34255         this.split.orientation = Roo.SplitBar.VERTICAL;
34256         this.split.el.addClass("x-layout-split-v");
34257     }
34258     var size = config.initialSize || config.height;
34259     if(typeof size != "undefined"){
34260         this.el.setHeight(size);
34261     }
34262 };
34263 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34264     orientation: Roo.SplitBar.VERTICAL,
34265     getBox : function(){
34266         if(this.collapsed){
34267             return this.collapsedEl.getBox();
34268         }
34269         var box = this.el.getBox();
34270         if(this.split){
34271             box.height += this.split.el.getHeight();
34272         }
34273         return box;
34274     },
34275     
34276     updateBox : function(box){
34277         if(this.split && !this.collapsed){
34278             box.height -= this.split.el.getHeight();
34279             this.split.el.setLeft(box.x);
34280             this.split.el.setTop(box.y+box.height);
34281             this.split.el.setWidth(box.width);
34282         }
34283         if(this.collapsed){
34284             this.updateBody(box.width, null);
34285         }
34286         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34287     }
34288 });
34289
34290 Roo.SouthLayoutRegion = function(mgr, config){
34291     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34292     if(this.split){
34293         this.split.placement = Roo.SplitBar.BOTTOM;
34294         this.split.orientation = Roo.SplitBar.VERTICAL;
34295         this.split.el.addClass("x-layout-split-v");
34296     }
34297     var size = config.initialSize || config.height;
34298     if(typeof size != "undefined"){
34299         this.el.setHeight(size);
34300     }
34301 };
34302 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34303     orientation: Roo.SplitBar.VERTICAL,
34304     getBox : function(){
34305         if(this.collapsed){
34306             return this.collapsedEl.getBox();
34307         }
34308         var box = this.el.getBox();
34309         if(this.split){
34310             var sh = this.split.el.getHeight();
34311             box.height += sh;
34312             box.y -= sh;
34313         }
34314         return box;
34315     },
34316     
34317     updateBox : function(box){
34318         if(this.split && !this.collapsed){
34319             var sh = this.split.el.getHeight();
34320             box.height -= sh;
34321             box.y += sh;
34322             this.split.el.setLeft(box.x);
34323             this.split.el.setTop(box.y-sh);
34324             this.split.el.setWidth(box.width);
34325         }
34326         if(this.collapsed){
34327             this.updateBody(box.width, null);
34328         }
34329         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34330     }
34331 });
34332
34333 Roo.EastLayoutRegion = function(mgr, config){
34334     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34335     if(this.split){
34336         this.split.placement = Roo.SplitBar.RIGHT;
34337         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34338         this.split.el.addClass("x-layout-split-h");
34339     }
34340     var size = config.initialSize || config.width;
34341     if(typeof size != "undefined"){
34342         this.el.setWidth(size);
34343     }
34344 };
34345 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34346     orientation: Roo.SplitBar.HORIZONTAL,
34347     getBox : function(){
34348         if(this.collapsed){
34349             return this.collapsedEl.getBox();
34350         }
34351         var box = this.el.getBox();
34352         if(this.split){
34353             var sw = this.split.el.getWidth();
34354             box.width += sw;
34355             box.x -= sw;
34356         }
34357         return box;
34358     },
34359
34360     updateBox : function(box){
34361         if(this.split && !this.collapsed){
34362             var sw = this.split.el.getWidth();
34363             box.width -= sw;
34364             this.split.el.setLeft(box.x);
34365             this.split.el.setTop(box.y);
34366             this.split.el.setHeight(box.height);
34367             box.x += sw;
34368         }
34369         if(this.collapsed){
34370             this.updateBody(null, box.height);
34371         }
34372         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34373     }
34374 });
34375
34376 Roo.WestLayoutRegion = function(mgr, config){
34377     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34378     if(this.split){
34379         this.split.placement = Roo.SplitBar.LEFT;
34380         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34381         this.split.el.addClass("x-layout-split-h");
34382     }
34383     var size = config.initialSize || config.width;
34384     if(typeof size != "undefined"){
34385         this.el.setWidth(size);
34386     }
34387 };
34388 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34389     orientation: Roo.SplitBar.HORIZONTAL,
34390     getBox : function(){
34391         if(this.collapsed){
34392             return this.collapsedEl.getBox();
34393         }
34394         var box = this.el.getBox();
34395         if(this.split){
34396             box.width += this.split.el.getWidth();
34397         }
34398         return box;
34399     },
34400     
34401     updateBox : function(box){
34402         if(this.split && !this.collapsed){
34403             var sw = this.split.el.getWidth();
34404             box.width -= sw;
34405             this.split.el.setLeft(box.x+box.width);
34406             this.split.el.setTop(box.y);
34407             this.split.el.setHeight(box.height);
34408         }
34409         if(this.collapsed){
34410             this.updateBody(null, box.height);
34411         }
34412         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34413     }
34414 });
34415 /*
34416  * Based on:
34417  * Ext JS Library 1.1.1
34418  * Copyright(c) 2006-2007, Ext JS, LLC.
34419  *
34420  * Originally Released Under LGPL - original licence link has changed is not relivant.
34421  *
34422  * Fork - LGPL
34423  * <script type="text/javascript">
34424  */
34425  
34426  
34427 /*
34428  * Private internal class for reading and applying state
34429  */
34430 Roo.LayoutStateManager = function(layout){
34431      // default empty state
34432      this.state = {
34433         north: {},
34434         south: {},
34435         east: {},
34436         west: {}       
34437     };
34438 };
34439
34440 Roo.LayoutStateManager.prototype = {
34441     init : function(layout, provider){
34442         this.provider = provider;
34443         var state = provider.get(layout.id+"-layout-state");
34444         if(state){
34445             var wasUpdating = layout.isUpdating();
34446             if(!wasUpdating){
34447                 layout.beginUpdate();
34448             }
34449             for(var key in state){
34450                 if(typeof state[key] != "function"){
34451                     var rstate = state[key];
34452                     var r = layout.getRegion(key);
34453                     if(r && rstate){
34454                         if(rstate.size){
34455                             r.resizeTo(rstate.size);
34456                         }
34457                         if(rstate.collapsed == true){
34458                             r.collapse(true);
34459                         }else{
34460                             r.expand(null, true);
34461                         }
34462                     }
34463                 }
34464             }
34465             if(!wasUpdating){
34466                 layout.endUpdate();
34467             }
34468             this.state = state; 
34469         }
34470         this.layout = layout;
34471         layout.on("regionresized", this.onRegionResized, this);
34472         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34473         layout.on("regionexpanded", this.onRegionExpanded, this);
34474     },
34475     
34476     storeState : function(){
34477         this.provider.set(this.layout.id+"-layout-state", this.state);
34478     },
34479     
34480     onRegionResized : function(region, newSize){
34481         this.state[region.getPosition()].size = newSize;
34482         this.storeState();
34483     },
34484     
34485     onRegionCollapsed : function(region){
34486         this.state[region.getPosition()].collapsed = true;
34487         this.storeState();
34488     },
34489     
34490     onRegionExpanded : function(region){
34491         this.state[region.getPosition()].collapsed = false;
34492         this.storeState();
34493     }
34494 };/*
34495  * Based on:
34496  * Ext JS Library 1.1.1
34497  * Copyright(c) 2006-2007, Ext JS, LLC.
34498  *
34499  * Originally Released Under LGPL - original licence link has changed is not relivant.
34500  *
34501  * Fork - LGPL
34502  * <script type="text/javascript">
34503  */
34504 /**
34505  * @class Roo.ContentPanel
34506  * @extends Roo.util.Observable
34507  * A basic ContentPanel element.
34508  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34509  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34510  * @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
34511  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34512  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34513  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34514  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34515  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34516  * @cfg {String} title          The title for this panel
34517  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34518  * @cfg {String} url            Calls {@link #setUrl} with this value
34519  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34520  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34521  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34522  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34523
34524  * @constructor
34525  * Create a new ContentPanel.
34526  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34527  * @param {String/Object} config A string to set only the title or a config object
34528  * @param {String} content (optional) Set the HTML content for this panel
34529  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34530  */
34531 Roo.ContentPanel = function(el, config, content){
34532     
34533      
34534     /*
34535     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34536         config = el;
34537         el = Roo.id();
34538     }
34539     if (config && config.parentLayout) { 
34540         el = config.parentLayout.el.createChild(); 
34541     }
34542     */
34543     if(el.autoCreate){ // xtype is available if this is called from factory
34544         config = el;
34545         el = Roo.id();
34546     }
34547     this.el = Roo.get(el);
34548     if(!this.el && config && config.autoCreate){
34549         if(typeof config.autoCreate == "object"){
34550             if(!config.autoCreate.id){
34551                 config.autoCreate.id = config.id||el;
34552             }
34553             this.el = Roo.DomHelper.append(document.body,
34554                         config.autoCreate, true);
34555         }else{
34556             this.el = Roo.DomHelper.append(document.body,
34557                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34558         }
34559     }
34560     this.closable = false;
34561     this.loaded = false;
34562     this.active = false;
34563     if(typeof config == "string"){
34564         this.title = config;
34565     }else{
34566         Roo.apply(this, config);
34567     }
34568     
34569     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34570         this.wrapEl = this.el.wrap();
34571         this.toolbar.container = this.el.insertSibling(false, 'before');
34572         this.toolbar = new Roo.Toolbar(this.toolbar);
34573     }
34574     
34575     // xtype created footer. - not sure if will work as we normally have to render first..
34576     if (this.footer && !this.footer.el && this.footer.xtype) {
34577         if (!this.wrapEl) {
34578             this.wrapEl = this.el.wrap();
34579         }
34580     
34581         this.footer.container = this.wrapEl.createChild();
34582          
34583         this.footer = Roo.factory(this.footer, Roo);
34584         
34585     }
34586     
34587     if(this.resizeEl){
34588         this.resizeEl = Roo.get(this.resizeEl, true);
34589     }else{
34590         this.resizeEl = this.el;
34591     }
34592     // handle view.xtype
34593     
34594  
34595     
34596     
34597     this.addEvents({
34598         /**
34599          * @event activate
34600          * Fires when this panel is activated. 
34601          * @param {Roo.ContentPanel} this
34602          */
34603         "activate" : true,
34604         /**
34605          * @event deactivate
34606          * Fires when this panel is activated. 
34607          * @param {Roo.ContentPanel} this
34608          */
34609         "deactivate" : true,
34610
34611         /**
34612          * @event resize
34613          * Fires when this panel is resized if fitToFrame is true.
34614          * @param {Roo.ContentPanel} this
34615          * @param {Number} width The width after any component adjustments
34616          * @param {Number} height The height after any component adjustments
34617          */
34618         "resize" : true,
34619         
34620          /**
34621          * @event render
34622          * Fires when this tab is created
34623          * @param {Roo.ContentPanel} this
34624          */
34625         "render" : true
34626         
34627         
34628         
34629     });
34630     
34631
34632     
34633     
34634     if(this.autoScroll){
34635         this.resizeEl.setStyle("overflow", "auto");
34636     } else {
34637         // fix randome scrolling
34638         this.el.on('scroll', function() {
34639             Roo.log('fix random scolling');
34640             this.scrollTo('top',0); 
34641         });
34642     }
34643     content = content || this.content;
34644     if(content){
34645         this.setContent(content);
34646     }
34647     if(config && config.url){
34648         this.setUrl(this.url, this.params, this.loadOnce);
34649     }
34650     
34651     
34652     
34653     Roo.ContentPanel.superclass.constructor.call(this);
34654     
34655     if (this.view && typeof(this.view.xtype) != 'undefined') {
34656         this.view.el = this.el.appendChild(document.createElement("div"));
34657         this.view = Roo.factory(this.view); 
34658         this.view.render  &&  this.view.render(false, '');  
34659     }
34660     
34661     
34662     this.fireEvent('render', this);
34663 };
34664
34665 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34666     tabTip:'',
34667     setRegion : function(region){
34668         this.region = region;
34669         if(region){
34670            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34671         }else{
34672            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34673         } 
34674     },
34675     
34676     /**
34677      * Returns the toolbar for this Panel if one was configured. 
34678      * @return {Roo.Toolbar} 
34679      */
34680     getToolbar : function(){
34681         return this.toolbar;
34682     },
34683     
34684     setActiveState : function(active){
34685         this.active = active;
34686         if(!active){
34687             this.fireEvent("deactivate", this);
34688         }else{
34689             this.fireEvent("activate", this);
34690         }
34691     },
34692     /**
34693      * Updates this panel's element
34694      * @param {String} content The new content
34695      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34696     */
34697     setContent : function(content, loadScripts){
34698         this.el.update(content, loadScripts);
34699     },
34700
34701     ignoreResize : function(w, h){
34702         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34703             return true;
34704         }else{
34705             this.lastSize = {width: w, height: h};
34706             return false;
34707         }
34708     },
34709     /**
34710      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34711      * @return {Roo.UpdateManager} The UpdateManager
34712      */
34713     getUpdateManager : function(){
34714         return this.el.getUpdateManager();
34715     },
34716      /**
34717      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34718      * @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:
34719 <pre><code>
34720 panel.load({
34721     url: "your-url.php",
34722     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34723     callback: yourFunction,
34724     scope: yourObject, //(optional scope)
34725     discardUrl: false,
34726     nocache: false,
34727     text: "Loading...",
34728     timeout: 30,
34729     scripts: false
34730 });
34731 </code></pre>
34732      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34733      * 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.
34734      * @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}
34735      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34736      * @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.
34737      * @return {Roo.ContentPanel} this
34738      */
34739     load : function(){
34740         var um = this.el.getUpdateManager();
34741         um.update.apply(um, arguments);
34742         return this;
34743     },
34744
34745
34746     /**
34747      * 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.
34748      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34749      * @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)
34750      * @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)
34751      * @return {Roo.UpdateManager} The UpdateManager
34752      */
34753     setUrl : function(url, params, loadOnce){
34754         if(this.refreshDelegate){
34755             this.removeListener("activate", this.refreshDelegate);
34756         }
34757         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34758         this.on("activate", this.refreshDelegate);
34759         return this.el.getUpdateManager();
34760     },
34761     
34762     _handleRefresh : function(url, params, loadOnce){
34763         if(!loadOnce || !this.loaded){
34764             var updater = this.el.getUpdateManager();
34765             updater.update(url, params, this._setLoaded.createDelegate(this));
34766         }
34767     },
34768     
34769     _setLoaded : function(){
34770         this.loaded = true;
34771     }, 
34772     
34773     /**
34774      * Returns this panel's id
34775      * @return {String} 
34776      */
34777     getId : function(){
34778         return this.el.id;
34779     },
34780     
34781     /** 
34782      * Returns this panel's element - used by regiosn to add.
34783      * @return {Roo.Element} 
34784      */
34785     getEl : function(){
34786         return this.wrapEl || this.el;
34787     },
34788     
34789     adjustForComponents : function(width, height)
34790     {
34791         //Roo.log('adjustForComponents ');
34792         if(this.resizeEl != this.el){
34793             width -= this.el.getFrameWidth('lr');
34794             height -= this.el.getFrameWidth('tb');
34795         }
34796         if(this.toolbar){
34797             var te = this.toolbar.getEl();
34798             height -= te.getHeight();
34799             te.setWidth(width);
34800         }
34801         if(this.footer){
34802             var te = this.footer.getEl();
34803             Roo.log("footer:" + te.getHeight());
34804             
34805             height -= te.getHeight();
34806             te.setWidth(width);
34807         }
34808         
34809         
34810         if(this.adjustments){
34811             width += this.adjustments[0];
34812             height += this.adjustments[1];
34813         }
34814         return {"width": width, "height": height};
34815     },
34816     
34817     setSize : function(width, height){
34818         if(this.fitToFrame && !this.ignoreResize(width, height)){
34819             if(this.fitContainer && this.resizeEl != this.el){
34820                 this.el.setSize(width, height);
34821             }
34822             var size = this.adjustForComponents(width, height);
34823             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34824             this.fireEvent('resize', this, size.width, size.height);
34825         }
34826     },
34827     
34828     /**
34829      * Returns this panel's title
34830      * @return {String} 
34831      */
34832     getTitle : function(){
34833         return this.title;
34834     },
34835     
34836     /**
34837      * Set this panel's title
34838      * @param {String} title
34839      */
34840     setTitle : function(title){
34841         this.title = title;
34842         if(this.region){
34843             this.region.updatePanelTitle(this, title);
34844         }
34845     },
34846     
34847     /**
34848      * Returns true is this panel was configured to be closable
34849      * @return {Boolean} 
34850      */
34851     isClosable : function(){
34852         return this.closable;
34853     },
34854     
34855     beforeSlide : function(){
34856         this.el.clip();
34857         this.resizeEl.clip();
34858     },
34859     
34860     afterSlide : function(){
34861         this.el.unclip();
34862         this.resizeEl.unclip();
34863     },
34864     
34865     /**
34866      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34867      *   Will fail silently if the {@link #setUrl} method has not been called.
34868      *   This does not activate the panel, just updates its content.
34869      */
34870     refresh : function(){
34871         if(this.refreshDelegate){
34872            this.loaded = false;
34873            this.refreshDelegate();
34874         }
34875     },
34876     
34877     /**
34878      * Destroys this panel
34879      */
34880     destroy : function(){
34881         this.el.removeAllListeners();
34882         var tempEl = document.createElement("span");
34883         tempEl.appendChild(this.el.dom);
34884         tempEl.innerHTML = "";
34885         this.el.remove();
34886         this.el = null;
34887     },
34888     
34889     /**
34890      * form - if the content panel contains a form - this is a reference to it.
34891      * @type {Roo.form.Form}
34892      */
34893     form : false,
34894     /**
34895      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34896      *    This contains a reference to it.
34897      * @type {Roo.View}
34898      */
34899     view : false,
34900     
34901       /**
34902      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34903      * <pre><code>
34904
34905 layout.addxtype({
34906        xtype : 'Form',
34907        items: [ .... ]
34908    }
34909 );
34910
34911 </code></pre>
34912      * @param {Object} cfg Xtype definition of item to add.
34913      */
34914     
34915     addxtype : function(cfg) {
34916         // add form..
34917         if (cfg.xtype.match(/^Form$/)) {
34918             
34919             var el;
34920             //if (this.footer) {
34921             //    el = this.footer.container.insertSibling(false, 'before');
34922             //} else {
34923                 el = this.el.createChild();
34924             //}
34925
34926             this.form = new  Roo.form.Form(cfg);
34927             
34928             
34929             if ( this.form.allItems.length) this.form.render(el.dom);
34930             return this.form;
34931         }
34932         // should only have one of theses..
34933         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34934             // views.. should not be just added - used named prop 'view''
34935             
34936             cfg.el = this.el.appendChild(document.createElement("div"));
34937             // factory?
34938             
34939             var ret = new Roo.factory(cfg);
34940              
34941              ret.render && ret.render(false, ''); // render blank..
34942             this.view = ret;
34943             return ret;
34944         }
34945         return false;
34946     }
34947 });
34948
34949 /**
34950  * @class Roo.GridPanel
34951  * @extends Roo.ContentPanel
34952  * @constructor
34953  * Create a new GridPanel.
34954  * @param {Roo.grid.Grid} grid The grid for this panel
34955  * @param {String/Object} config A string to set only the panel's title, or a config object
34956  */
34957 Roo.GridPanel = function(grid, config){
34958     
34959   
34960     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34961         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34962         
34963     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34964     
34965     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34966     
34967     if(this.toolbar){
34968         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34969     }
34970     // xtype created footer. - not sure if will work as we normally have to render first..
34971     if (this.footer && !this.footer.el && this.footer.xtype) {
34972         
34973         this.footer.container = this.grid.getView().getFooterPanel(true);
34974         this.footer.dataSource = this.grid.dataSource;
34975         this.footer = Roo.factory(this.footer, Roo);
34976         
34977     }
34978     
34979     grid.monitorWindowResize = false; // turn off autosizing
34980     grid.autoHeight = false;
34981     grid.autoWidth = false;
34982     this.grid = grid;
34983     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34984 };
34985
34986 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34987     getId : function(){
34988         return this.grid.id;
34989     },
34990     
34991     /**
34992      * Returns the grid for this panel
34993      * @return {Roo.grid.Grid} 
34994      */
34995     getGrid : function(){
34996         return this.grid;    
34997     },
34998     
34999     setSize : function(width, height){
35000         if(!this.ignoreResize(width, height)){
35001             var grid = this.grid;
35002             var size = this.adjustForComponents(width, height);
35003             grid.getGridEl().setSize(size.width, size.height);
35004             grid.autoSize();
35005         }
35006     },
35007     
35008     beforeSlide : function(){
35009         this.grid.getView().scroller.clip();
35010     },
35011     
35012     afterSlide : function(){
35013         this.grid.getView().scroller.unclip();
35014     },
35015     
35016     destroy : function(){
35017         this.grid.destroy();
35018         delete this.grid;
35019         Roo.GridPanel.superclass.destroy.call(this); 
35020     }
35021 });
35022
35023
35024 /**
35025  * @class Roo.NestedLayoutPanel
35026  * @extends Roo.ContentPanel
35027  * @constructor
35028  * Create a new NestedLayoutPanel.
35029  * 
35030  * 
35031  * @param {Roo.BorderLayout} layout The layout for this panel
35032  * @param {String/Object} config A string to set only the title or a config object
35033  */
35034 Roo.NestedLayoutPanel = function(layout, config)
35035 {
35036     // construct with only one argument..
35037     /* FIXME - implement nicer consturctors
35038     if (layout.layout) {
35039         config = layout;
35040         layout = config.layout;
35041         delete config.layout;
35042     }
35043     if (layout.xtype && !layout.getEl) {
35044         // then layout needs constructing..
35045         layout = Roo.factory(layout, Roo);
35046     }
35047     */
35048     
35049     
35050     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35051     
35052     layout.monitorWindowResize = false; // turn off autosizing
35053     this.layout = layout;
35054     this.layout.getEl().addClass("x-layout-nested-layout");
35055     
35056     
35057     
35058     
35059 };
35060
35061 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35062
35063     setSize : function(width, height){
35064         if(!this.ignoreResize(width, height)){
35065             var size = this.adjustForComponents(width, height);
35066             var el = this.layout.getEl();
35067             el.setSize(size.width, size.height);
35068             var touch = el.dom.offsetWidth;
35069             this.layout.layout();
35070             // ie requires a double layout on the first pass
35071             if(Roo.isIE && !this.initialized){
35072                 this.initialized = true;
35073                 this.layout.layout();
35074             }
35075         }
35076     },
35077     
35078     // activate all subpanels if not currently active..
35079     
35080     setActiveState : function(active){
35081         this.active = active;
35082         if(!active){
35083             this.fireEvent("deactivate", this);
35084             return;
35085         }
35086         
35087         this.fireEvent("activate", this);
35088         // not sure if this should happen before or after..
35089         if (!this.layout) {
35090             return; // should not happen..
35091         }
35092         var reg = false;
35093         for (var r in this.layout.regions) {
35094             reg = this.layout.getRegion(r);
35095             if (reg.getActivePanel()) {
35096                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35097                 reg.setActivePanel(reg.getActivePanel());
35098                 continue;
35099             }
35100             if (!reg.panels.length) {
35101                 continue;
35102             }
35103             reg.showPanel(reg.getPanel(0));
35104         }
35105         
35106         
35107         
35108         
35109     },
35110     
35111     /**
35112      * Returns the nested BorderLayout for this panel
35113      * @return {Roo.BorderLayout} 
35114      */
35115     getLayout : function(){
35116         return this.layout;
35117     },
35118     
35119      /**
35120      * Adds a xtype elements to the layout of the nested panel
35121      * <pre><code>
35122
35123 panel.addxtype({
35124        xtype : 'ContentPanel',
35125        region: 'west',
35126        items: [ .... ]
35127    }
35128 );
35129
35130 panel.addxtype({
35131         xtype : 'NestedLayoutPanel',
35132         region: 'west',
35133         layout: {
35134            center: { },
35135            west: { }   
35136         },
35137         items : [ ... list of content panels or nested layout panels.. ]
35138    }
35139 );
35140 </code></pre>
35141      * @param {Object} cfg Xtype definition of item to add.
35142      */
35143     addxtype : function(cfg) {
35144         return this.layout.addxtype(cfg);
35145     
35146     }
35147 });
35148
35149 Roo.ScrollPanel = function(el, config, content){
35150     config = config || {};
35151     config.fitToFrame = true;
35152     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35153     
35154     this.el.dom.style.overflow = "hidden";
35155     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35156     this.el.removeClass("x-layout-inactive-content");
35157     this.el.on("mousewheel", this.onWheel, this);
35158
35159     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35160     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35161     up.unselectable(); down.unselectable();
35162     up.on("click", this.scrollUp, this);
35163     down.on("click", this.scrollDown, this);
35164     up.addClassOnOver("x-scroller-btn-over");
35165     down.addClassOnOver("x-scroller-btn-over");
35166     up.addClassOnClick("x-scroller-btn-click");
35167     down.addClassOnClick("x-scroller-btn-click");
35168     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35169
35170     this.resizeEl = this.el;
35171     this.el = wrap; this.up = up; this.down = down;
35172 };
35173
35174 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35175     increment : 100,
35176     wheelIncrement : 5,
35177     scrollUp : function(){
35178         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35179     },
35180
35181     scrollDown : function(){
35182         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35183     },
35184
35185     afterScroll : function(){
35186         var el = this.resizeEl;
35187         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35188         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35189         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35190     },
35191
35192     setSize : function(){
35193         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35194         this.afterScroll();
35195     },
35196
35197     onWheel : function(e){
35198         var d = e.getWheelDelta();
35199         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35200         this.afterScroll();
35201         e.stopEvent();
35202     },
35203
35204     setContent : function(content, loadScripts){
35205         this.resizeEl.update(content, loadScripts);
35206     }
35207
35208 });
35209
35210
35211
35212
35213
35214
35215
35216
35217
35218 /**
35219  * @class Roo.TreePanel
35220  * @extends Roo.ContentPanel
35221  * @constructor
35222  * Create a new TreePanel. - defaults to fit/scoll contents.
35223  * @param {String/Object} config A string to set only the panel's title, or a config object
35224  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35225  */
35226 Roo.TreePanel = function(config){
35227     var el = config.el;
35228     var tree = config.tree;
35229     delete config.tree; 
35230     delete config.el; // hopefull!
35231     
35232     // wrapper for IE7 strict & safari scroll issue
35233     
35234     var treeEl = el.createChild();
35235     config.resizeEl = treeEl;
35236     
35237     
35238     
35239     Roo.TreePanel.superclass.constructor.call(this, el, config);
35240  
35241  
35242     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35243     //console.log(tree);
35244     this.on('activate', function()
35245     {
35246         if (this.tree.rendered) {
35247             return;
35248         }
35249         //console.log('render tree');
35250         this.tree.render();
35251     });
35252     // this should not be needed.. - it's actually the 'el' that resizes?
35253     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35254     
35255     //this.on('resize',  function (cp, w, h) {
35256     //        this.tree.innerCt.setWidth(w);
35257     //        this.tree.innerCt.setHeight(h);
35258     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35259     //});
35260
35261         
35262     
35263 };
35264
35265 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35266     fitToFrame : true,
35267     autoScroll : true
35268 });
35269
35270
35271
35272
35273
35274
35275
35276
35277
35278
35279
35280 /*
35281  * Based on:
35282  * Ext JS Library 1.1.1
35283  * Copyright(c) 2006-2007, Ext JS, LLC.
35284  *
35285  * Originally Released Under LGPL - original licence link has changed is not relivant.
35286  *
35287  * Fork - LGPL
35288  * <script type="text/javascript">
35289  */
35290  
35291
35292 /**
35293  * @class Roo.ReaderLayout
35294  * @extends Roo.BorderLayout
35295  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35296  * center region containing two nested regions (a top one for a list view and one for item preview below),
35297  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35298  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35299  * expedites the setup of the overall layout and regions for this common application style.
35300  * Example:
35301  <pre><code>
35302 var reader = new Roo.ReaderLayout();
35303 var CP = Roo.ContentPanel;  // shortcut for adding
35304
35305 reader.beginUpdate();
35306 reader.add("north", new CP("north", "North"));
35307 reader.add("west", new CP("west", {title: "West"}));
35308 reader.add("east", new CP("east", {title: "East"}));
35309
35310 reader.regions.listView.add(new CP("listView", "List"));
35311 reader.regions.preview.add(new CP("preview", "Preview"));
35312 reader.endUpdate();
35313 </code></pre>
35314 * @constructor
35315 * Create a new ReaderLayout
35316 * @param {Object} config Configuration options
35317 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35318 * document.body if omitted)
35319 */
35320 Roo.ReaderLayout = function(config, renderTo){
35321     var c = config || {size:{}};
35322     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35323         north: c.north !== false ? Roo.apply({
35324             split:false,
35325             initialSize: 32,
35326             titlebar: false
35327         }, c.north) : false,
35328         west: c.west !== false ? Roo.apply({
35329             split:true,
35330             initialSize: 200,
35331             minSize: 175,
35332             maxSize: 400,
35333             titlebar: true,
35334             collapsible: true,
35335             animate: true,
35336             margins:{left:5,right:0,bottom:5,top:5},
35337             cmargins:{left:5,right:5,bottom:5,top:5}
35338         }, c.west) : false,
35339         east: c.east !== false ? Roo.apply({
35340             split:true,
35341             initialSize: 200,
35342             minSize: 175,
35343             maxSize: 400,
35344             titlebar: true,
35345             collapsible: true,
35346             animate: true,
35347             margins:{left:0,right:5,bottom:5,top:5},
35348             cmargins:{left:5,right:5,bottom:5,top:5}
35349         }, c.east) : false,
35350         center: Roo.apply({
35351             tabPosition: 'top',
35352             autoScroll:false,
35353             closeOnTab: true,
35354             titlebar:false,
35355             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35356         }, c.center)
35357     });
35358
35359     this.el.addClass('x-reader');
35360
35361     this.beginUpdate();
35362
35363     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35364         south: c.preview !== false ? Roo.apply({
35365             split:true,
35366             initialSize: 200,
35367             minSize: 100,
35368             autoScroll:true,
35369             collapsible:true,
35370             titlebar: true,
35371             cmargins:{top:5,left:0, right:0, bottom:0}
35372         }, c.preview) : false,
35373         center: Roo.apply({
35374             autoScroll:false,
35375             titlebar:false,
35376             minHeight:200
35377         }, c.listView)
35378     });
35379     this.add('center', new Roo.NestedLayoutPanel(inner,
35380             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35381
35382     this.endUpdate();
35383
35384     this.regions.preview = inner.getRegion('south');
35385     this.regions.listView = inner.getRegion('center');
35386 };
35387
35388 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35389  * Based on:
35390  * Ext JS Library 1.1.1
35391  * Copyright(c) 2006-2007, Ext JS, LLC.
35392  *
35393  * Originally Released Under LGPL - original licence link has changed is not relivant.
35394  *
35395  * Fork - LGPL
35396  * <script type="text/javascript">
35397  */
35398  
35399 /**
35400  * @class Roo.grid.Grid
35401  * @extends Roo.util.Observable
35402  * This class represents the primary interface of a component based grid control.
35403  * <br><br>Usage:<pre><code>
35404  var grid = new Roo.grid.Grid("my-container-id", {
35405      ds: myDataStore,
35406      cm: myColModel,
35407      selModel: mySelectionModel,
35408      autoSizeColumns: true,
35409      monitorWindowResize: false,
35410      trackMouseOver: true
35411  });
35412  // set any options
35413  grid.render();
35414  * </code></pre>
35415  * <b>Common Problems:</b><br/>
35416  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35417  * element will correct this<br/>
35418  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35419  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35420  * are unpredictable.<br/>
35421  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35422  * grid to calculate dimensions/offsets.<br/>
35423   * @constructor
35424  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35425  * The container MUST have some type of size defined for the grid to fill. The container will be
35426  * automatically set to position relative if it isn't already.
35427  * @param {Object} config A config object that sets properties on this grid.
35428  */
35429 Roo.grid.Grid = function(container, config){
35430         // initialize the container
35431         this.container = Roo.get(container);
35432         this.container.update("");
35433         this.container.setStyle("overflow", "hidden");
35434     this.container.addClass('x-grid-container');
35435
35436     this.id = this.container.id;
35437
35438     Roo.apply(this, config);
35439     // check and correct shorthanded configs
35440     if(this.ds){
35441         this.dataSource = this.ds;
35442         delete this.ds;
35443     }
35444     if(this.cm){
35445         this.colModel = this.cm;
35446         delete this.cm;
35447     }
35448     if(this.sm){
35449         this.selModel = this.sm;
35450         delete this.sm;
35451     }
35452
35453     if (this.selModel) {
35454         this.selModel = Roo.factory(this.selModel, Roo.grid);
35455         this.sm = this.selModel;
35456         this.sm.xmodule = this.xmodule || false;
35457     }
35458     if (typeof(this.colModel.config) == 'undefined') {
35459         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35460         this.cm = this.colModel;
35461         this.cm.xmodule = this.xmodule || false;
35462     }
35463     if (this.dataSource) {
35464         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35465         this.ds = this.dataSource;
35466         this.ds.xmodule = this.xmodule || false;
35467          
35468     }
35469     
35470     
35471     
35472     if(this.width){
35473         this.container.setWidth(this.width);
35474     }
35475
35476     if(this.height){
35477         this.container.setHeight(this.height);
35478     }
35479     /** @private */
35480         this.addEvents({
35481         // raw events
35482         /**
35483          * @event click
35484          * The raw click event for the entire grid.
35485          * @param {Roo.EventObject} e
35486          */
35487         "click" : true,
35488         /**
35489          * @event dblclick
35490          * The raw dblclick event for the entire grid.
35491          * @param {Roo.EventObject} e
35492          */
35493         "dblclick" : true,
35494         /**
35495          * @event contextmenu
35496          * The raw contextmenu event for the entire grid.
35497          * @param {Roo.EventObject} e
35498          */
35499         "contextmenu" : true,
35500         /**
35501          * @event mousedown
35502          * The raw mousedown event for the entire grid.
35503          * @param {Roo.EventObject} e
35504          */
35505         "mousedown" : true,
35506         /**
35507          * @event mouseup
35508          * The raw mouseup event for the entire grid.
35509          * @param {Roo.EventObject} e
35510          */
35511         "mouseup" : true,
35512         /**
35513          * @event mouseover
35514          * The raw mouseover event for the entire grid.
35515          * @param {Roo.EventObject} e
35516          */
35517         "mouseover" : true,
35518         /**
35519          * @event mouseout
35520          * The raw mouseout event for the entire grid.
35521          * @param {Roo.EventObject} e
35522          */
35523         "mouseout" : true,
35524         /**
35525          * @event keypress
35526          * The raw keypress event for the entire grid.
35527          * @param {Roo.EventObject} e
35528          */
35529         "keypress" : true,
35530         /**
35531          * @event keydown
35532          * The raw keydown event for the entire grid.
35533          * @param {Roo.EventObject} e
35534          */
35535         "keydown" : true,
35536
35537         // custom events
35538
35539         /**
35540          * @event cellclick
35541          * Fires when a cell is clicked
35542          * @param {Grid} this
35543          * @param {Number} rowIndex
35544          * @param {Number} columnIndex
35545          * @param {Roo.EventObject} e
35546          */
35547         "cellclick" : true,
35548         /**
35549          * @event celldblclick
35550          * Fires when a cell is double clicked
35551          * @param {Grid} this
35552          * @param {Number} rowIndex
35553          * @param {Number} columnIndex
35554          * @param {Roo.EventObject} e
35555          */
35556         "celldblclick" : true,
35557         /**
35558          * @event rowclick
35559          * Fires when a row is clicked
35560          * @param {Grid} this
35561          * @param {Number} rowIndex
35562          * @param {Roo.EventObject} e
35563          */
35564         "rowclick" : true,
35565         /**
35566          * @event rowdblclick
35567          * Fires when a row is double clicked
35568          * @param {Grid} this
35569          * @param {Number} rowIndex
35570          * @param {Roo.EventObject} e
35571          */
35572         "rowdblclick" : true,
35573         /**
35574          * @event headerclick
35575          * Fires when a header is clicked
35576          * @param {Grid} this
35577          * @param {Number} columnIndex
35578          * @param {Roo.EventObject} e
35579          */
35580         "headerclick" : true,
35581         /**
35582          * @event headerdblclick
35583          * Fires when a header cell is double clicked
35584          * @param {Grid} this
35585          * @param {Number} columnIndex
35586          * @param {Roo.EventObject} e
35587          */
35588         "headerdblclick" : true,
35589         /**
35590          * @event rowcontextmenu
35591          * Fires when a row is right clicked
35592          * @param {Grid} this
35593          * @param {Number} rowIndex
35594          * @param {Roo.EventObject} e
35595          */
35596         "rowcontextmenu" : true,
35597         /**
35598          * @event cellcontextmenu
35599          * Fires when a cell is right clicked
35600          * @param {Grid} this
35601          * @param {Number} rowIndex
35602          * @param {Number} cellIndex
35603          * @param {Roo.EventObject} e
35604          */
35605          "cellcontextmenu" : true,
35606         /**
35607          * @event headercontextmenu
35608          * Fires when a header is right clicked
35609          * @param {Grid} this
35610          * @param {Number} columnIndex
35611          * @param {Roo.EventObject} e
35612          */
35613         "headercontextmenu" : true,
35614         /**
35615          * @event bodyscroll
35616          * Fires when the body element is scrolled
35617          * @param {Number} scrollLeft
35618          * @param {Number} scrollTop
35619          */
35620         "bodyscroll" : true,
35621         /**
35622          * @event columnresize
35623          * Fires when the user resizes a column
35624          * @param {Number} columnIndex
35625          * @param {Number} newSize
35626          */
35627         "columnresize" : true,
35628         /**
35629          * @event columnmove
35630          * Fires when the user moves a column
35631          * @param {Number} oldIndex
35632          * @param {Number} newIndex
35633          */
35634         "columnmove" : true,
35635         /**
35636          * @event startdrag
35637          * Fires when row(s) start being dragged
35638          * @param {Grid} this
35639          * @param {Roo.GridDD} dd The drag drop object
35640          * @param {event} e The raw browser event
35641          */
35642         "startdrag" : true,
35643         /**
35644          * @event enddrag
35645          * Fires when a drag operation is complete
35646          * @param {Grid} this
35647          * @param {Roo.GridDD} dd The drag drop object
35648          * @param {event} e The raw browser event
35649          */
35650         "enddrag" : true,
35651         /**
35652          * @event dragdrop
35653          * Fires when dragged row(s) are dropped on a valid DD target
35654          * @param {Grid} this
35655          * @param {Roo.GridDD} dd The drag drop object
35656          * @param {String} targetId The target drag drop object
35657          * @param {event} e The raw browser event
35658          */
35659         "dragdrop" : true,
35660         /**
35661          * @event dragover
35662          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35663          * @param {Grid} this
35664          * @param {Roo.GridDD} dd The drag drop object
35665          * @param {String} targetId The target drag drop object
35666          * @param {event} e The raw browser event
35667          */
35668         "dragover" : true,
35669         /**
35670          * @event dragenter
35671          *  Fires when the dragged row(s) first cross another DD target while being dragged
35672          * @param {Grid} this
35673          * @param {Roo.GridDD} dd The drag drop object
35674          * @param {String} targetId The target drag drop object
35675          * @param {event} e The raw browser event
35676          */
35677         "dragenter" : true,
35678         /**
35679          * @event dragout
35680          * Fires when the dragged row(s) leave another DD target while being dragged
35681          * @param {Grid} this
35682          * @param {Roo.GridDD} dd The drag drop object
35683          * @param {String} targetId The target drag drop object
35684          * @param {event} e The raw browser event
35685          */
35686         "dragout" : true,
35687         /**
35688          * @event rowclass
35689          * Fires when a row is rendered, so you can change add a style to it.
35690          * @param {GridView} gridview   The grid view
35691          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35692          */
35693         'rowclass' : true,
35694
35695         /**
35696          * @event render
35697          * Fires when the grid is rendered
35698          * @param {Grid} grid
35699          */
35700         'render' : true
35701     });
35702
35703     Roo.grid.Grid.superclass.constructor.call(this);
35704 };
35705 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35706     
35707     /**
35708      * @cfg {String} ddGroup - drag drop group.
35709      */
35710
35711     /**
35712      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35713      */
35714     minColumnWidth : 25,
35715
35716     /**
35717      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35718      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35719      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35720      */
35721     autoSizeColumns : false,
35722
35723     /**
35724      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35725      */
35726     autoSizeHeaders : true,
35727
35728     /**
35729      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35730      */
35731     monitorWindowResize : true,
35732
35733     /**
35734      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35735      * rows measured to get a columns size. Default is 0 (all rows).
35736      */
35737     maxRowsToMeasure : 0,
35738
35739     /**
35740      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35741      */
35742     trackMouseOver : true,
35743
35744     /**
35745     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35746     */
35747     
35748     /**
35749     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35750     */
35751     enableDragDrop : false,
35752     
35753     /**
35754     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35755     */
35756     enableColumnMove : true,
35757     
35758     /**
35759     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35760     */
35761     enableColumnHide : true,
35762     
35763     /**
35764     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35765     */
35766     enableRowHeightSync : false,
35767     
35768     /**
35769     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35770     */
35771     stripeRows : true,
35772     
35773     /**
35774     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35775     */
35776     autoHeight : false,
35777
35778     /**
35779      * @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.
35780      */
35781     autoExpandColumn : false,
35782
35783     /**
35784     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35785     * Default is 50.
35786     */
35787     autoExpandMin : 50,
35788
35789     /**
35790     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35791     */
35792     autoExpandMax : 1000,
35793
35794     /**
35795     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35796     */
35797     view : null,
35798
35799     /**
35800     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35801     */
35802     loadMask : false,
35803     /**
35804     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35805     */
35806     dropTarget: false,
35807     
35808    
35809     
35810     // private
35811     rendered : false,
35812
35813     /**
35814     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35815     * of a fixed width. Default is false.
35816     */
35817     /**
35818     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35819     */
35820     /**
35821      * Called once after all setup has been completed and the grid is ready to be rendered.
35822      * @return {Roo.grid.Grid} this
35823      */
35824     render : function()
35825     {
35826         var c = this.container;
35827         // try to detect autoHeight/width mode
35828         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35829             this.autoHeight = true;
35830         }
35831         var view = this.getView();
35832         view.init(this);
35833
35834         c.on("click", this.onClick, this);
35835         c.on("dblclick", this.onDblClick, this);
35836         c.on("contextmenu", this.onContextMenu, this);
35837         c.on("keydown", this.onKeyDown, this);
35838         if (Roo.isTouch) {
35839             c.on("touchstart", this.onTouchStart, this);
35840         }
35841
35842         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35843
35844         this.getSelectionModel().init(this);
35845
35846         view.render();
35847
35848         if(this.loadMask){
35849             this.loadMask = new Roo.LoadMask(this.container,
35850                     Roo.apply({store:this.dataSource}, this.loadMask));
35851         }
35852         
35853         
35854         if (this.toolbar && this.toolbar.xtype) {
35855             this.toolbar.container = this.getView().getHeaderPanel(true);
35856             this.toolbar = new Roo.Toolbar(this.toolbar);
35857         }
35858         if (this.footer && this.footer.xtype) {
35859             this.footer.dataSource = this.getDataSource();
35860             this.footer.container = this.getView().getFooterPanel(true);
35861             this.footer = Roo.factory(this.footer, Roo);
35862         }
35863         if (this.dropTarget && this.dropTarget.xtype) {
35864             delete this.dropTarget.xtype;
35865             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35866         }
35867         
35868         
35869         this.rendered = true;
35870         this.fireEvent('render', this);
35871         return this;
35872     },
35873
35874         /**
35875          * Reconfigures the grid to use a different Store and Column Model.
35876          * The View will be bound to the new objects and refreshed.
35877          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35878          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35879          */
35880     reconfigure : function(dataSource, colModel){
35881         if(this.loadMask){
35882             this.loadMask.destroy();
35883             this.loadMask = new Roo.LoadMask(this.container,
35884                     Roo.apply({store:dataSource}, this.loadMask));
35885         }
35886         this.view.bind(dataSource, colModel);
35887         this.dataSource = dataSource;
35888         this.colModel = colModel;
35889         this.view.refresh(true);
35890     },
35891
35892     // private
35893     onKeyDown : function(e){
35894         this.fireEvent("keydown", e);
35895     },
35896
35897     /**
35898      * Destroy this grid.
35899      * @param {Boolean} removeEl True to remove the element
35900      */
35901     destroy : function(removeEl, keepListeners){
35902         if(this.loadMask){
35903             this.loadMask.destroy();
35904         }
35905         var c = this.container;
35906         c.removeAllListeners();
35907         this.view.destroy();
35908         this.colModel.purgeListeners();
35909         if(!keepListeners){
35910             this.purgeListeners();
35911         }
35912         c.update("");
35913         if(removeEl === true){
35914             c.remove();
35915         }
35916     },
35917
35918     // private
35919     processEvent : function(name, e){
35920         // does this fire select???
35921         Roo.log('grid:processEvent '  + name);
35922         
35923         if (name != 'touchstart' ) {
35924             this.fireEvent(name, e);    
35925         }
35926         
35927         var t = e.getTarget();
35928         var v = this.view;
35929         var header = v.findHeaderIndex(t);
35930         if(header !== false){
35931             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35932         }else{
35933             var row = v.findRowIndex(t);
35934             var cell = v.findCellIndex(t);
35935             if (name == 'touchstart') {
35936                 // first touch is always a click.
35937                 // hopefull this happens after selection is updated.?
35938                 name = false;
35939                 
35940                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35941                     var cs = this.selModel.getSelectedCell();
35942                     if (row == cs[0] && cell == cs[1]){
35943                         name = 'dblclick';
35944                     }
35945                 }
35946                 if (typeof(this.selModel.getSelections) != 'undefined') {
35947                     var cs = this.selModel.getSelections();
35948                     var ds = this.dataSource;
35949                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35950                         name = 'dblclick';
35951                     }
35952                 }
35953                 if (!name) {
35954                     return;
35955                 }
35956             }
35957             
35958             
35959             if(row !== false){
35960                 this.fireEvent("row" + name, this, row, e);
35961                 if(cell !== false){
35962                     this.fireEvent("cell" + name, this, row, cell, e);
35963                 }
35964             }
35965         }
35966     },
35967
35968     // private
35969     onClick : function(e){
35970         this.processEvent("click", e);
35971     },
35972    // private
35973     onTouchStart : function(e){
35974         this.processEvent("touchstart", e);
35975     },
35976
35977     // private
35978     onContextMenu : function(e, t){
35979         this.processEvent("contextmenu", e);
35980     },
35981
35982     // private
35983     onDblClick : function(e){
35984         this.processEvent("dblclick", e);
35985     },
35986
35987     // private
35988     walkCells : function(row, col, step, fn, scope){
35989         var cm = this.colModel, clen = cm.getColumnCount();
35990         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35991         if(step < 0){
35992             if(col < 0){
35993                 row--;
35994                 first = false;
35995             }
35996             while(row >= 0){
35997                 if(!first){
35998                     col = clen-1;
35999                 }
36000                 first = false;
36001                 while(col >= 0){
36002                     if(fn.call(scope || this, row, col, cm) === true){
36003                         return [row, col];
36004                     }
36005                     col--;
36006                 }
36007                 row--;
36008             }
36009         } else {
36010             if(col >= clen){
36011                 row++;
36012                 first = false;
36013             }
36014             while(row < rlen){
36015                 if(!first){
36016                     col = 0;
36017                 }
36018                 first = false;
36019                 while(col < clen){
36020                     if(fn.call(scope || this, row, col, cm) === true){
36021                         return [row, col];
36022                     }
36023                     col++;
36024                 }
36025                 row++;
36026             }
36027         }
36028         return null;
36029     },
36030
36031     // private
36032     getSelections : function(){
36033         return this.selModel.getSelections();
36034     },
36035
36036     /**
36037      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36038      * but if manual update is required this method will initiate it.
36039      */
36040     autoSize : function(){
36041         if(this.rendered){
36042             this.view.layout();
36043             if(this.view.adjustForScroll){
36044                 this.view.adjustForScroll();
36045             }
36046         }
36047     },
36048
36049     /**
36050      * Returns the grid's underlying element.
36051      * @return {Element} The element
36052      */
36053     getGridEl : function(){
36054         return this.container;
36055     },
36056
36057     // private for compatibility, overridden by editor grid
36058     stopEditing : function(){},
36059
36060     /**
36061      * Returns the grid's SelectionModel.
36062      * @return {SelectionModel}
36063      */
36064     getSelectionModel : function(){
36065         if(!this.selModel){
36066             this.selModel = new Roo.grid.RowSelectionModel();
36067         }
36068         return this.selModel;
36069     },
36070
36071     /**
36072      * Returns the grid's DataSource.
36073      * @return {DataSource}
36074      */
36075     getDataSource : function(){
36076         return this.dataSource;
36077     },
36078
36079     /**
36080      * Returns the grid's ColumnModel.
36081      * @return {ColumnModel}
36082      */
36083     getColumnModel : function(){
36084         return this.colModel;
36085     },
36086
36087     /**
36088      * Returns the grid's GridView object.
36089      * @return {GridView}
36090      */
36091     getView : function(){
36092         if(!this.view){
36093             this.view = new Roo.grid.GridView(this.viewConfig);
36094         }
36095         return this.view;
36096     },
36097     /**
36098      * Called to get grid's drag proxy text, by default returns this.ddText.
36099      * @return {String}
36100      */
36101     getDragDropText : function(){
36102         var count = this.selModel.getCount();
36103         return String.format(this.ddText, count, count == 1 ? '' : 's');
36104     }
36105 });
36106 /**
36107  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36108  * %0 is replaced with the number of selected rows.
36109  * @type String
36110  */
36111 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36112  * Based on:
36113  * Ext JS Library 1.1.1
36114  * Copyright(c) 2006-2007, Ext JS, LLC.
36115  *
36116  * Originally Released Under LGPL - original licence link has changed is not relivant.
36117  *
36118  * Fork - LGPL
36119  * <script type="text/javascript">
36120  */
36121  
36122 Roo.grid.AbstractGridView = function(){
36123         this.grid = null;
36124         
36125         this.events = {
36126             "beforerowremoved" : true,
36127             "beforerowsinserted" : true,
36128             "beforerefresh" : true,
36129             "rowremoved" : true,
36130             "rowsinserted" : true,
36131             "rowupdated" : true,
36132             "refresh" : true
36133         };
36134     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36135 };
36136
36137 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36138     rowClass : "x-grid-row",
36139     cellClass : "x-grid-cell",
36140     tdClass : "x-grid-td",
36141     hdClass : "x-grid-hd",
36142     splitClass : "x-grid-hd-split",
36143     
36144         init: function(grid){
36145         this.grid = grid;
36146                 var cid = this.grid.getGridEl().id;
36147         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36148         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36149         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36150         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36151         },
36152         
36153         getColumnRenderers : function(){
36154         var renderers = [];
36155         var cm = this.grid.colModel;
36156         var colCount = cm.getColumnCount();
36157         for(var i = 0; i < colCount; i++){
36158             renderers[i] = cm.getRenderer(i);
36159         }
36160         return renderers;
36161     },
36162     
36163     getColumnIds : function(){
36164         var ids = [];
36165         var cm = this.grid.colModel;
36166         var colCount = cm.getColumnCount();
36167         for(var i = 0; i < colCount; i++){
36168             ids[i] = cm.getColumnId(i);
36169         }
36170         return ids;
36171     },
36172     
36173     getDataIndexes : function(){
36174         if(!this.indexMap){
36175             this.indexMap = this.buildIndexMap();
36176         }
36177         return this.indexMap.colToData;
36178     },
36179     
36180     getColumnIndexByDataIndex : function(dataIndex){
36181         if(!this.indexMap){
36182             this.indexMap = this.buildIndexMap();
36183         }
36184         return this.indexMap.dataToCol[dataIndex];
36185     },
36186     
36187     /**
36188      * Set a css style for a column dynamically. 
36189      * @param {Number} colIndex The index of the column
36190      * @param {String} name The css property name
36191      * @param {String} value The css value
36192      */
36193     setCSSStyle : function(colIndex, name, value){
36194         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36195         Roo.util.CSS.updateRule(selector, name, value);
36196     },
36197     
36198     generateRules : function(cm){
36199         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36200         Roo.util.CSS.removeStyleSheet(rulesId);
36201         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36202             var cid = cm.getColumnId(i);
36203             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36204                          this.tdSelector, cid, " {\n}\n",
36205                          this.hdSelector, cid, " {\n}\n",
36206                          this.splitSelector, cid, " {\n}\n");
36207         }
36208         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36209     }
36210 });/*
36211  * Based on:
36212  * Ext JS Library 1.1.1
36213  * Copyright(c) 2006-2007, Ext JS, LLC.
36214  *
36215  * Originally Released Under LGPL - original licence link has changed is not relivant.
36216  *
36217  * Fork - LGPL
36218  * <script type="text/javascript">
36219  */
36220
36221 // private
36222 // This is a support class used internally by the Grid components
36223 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36224     this.grid = grid;
36225     this.view = grid.getView();
36226     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36227     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36228     if(hd2){
36229         this.setHandleElId(Roo.id(hd));
36230         this.setOuterHandleElId(Roo.id(hd2));
36231     }
36232     this.scroll = false;
36233 };
36234 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36235     maxDragWidth: 120,
36236     getDragData : function(e){
36237         var t = Roo.lib.Event.getTarget(e);
36238         var h = this.view.findHeaderCell(t);
36239         if(h){
36240             return {ddel: h.firstChild, header:h};
36241         }
36242         return false;
36243     },
36244
36245     onInitDrag : function(e){
36246         this.view.headersDisabled = true;
36247         var clone = this.dragData.ddel.cloneNode(true);
36248         clone.id = Roo.id();
36249         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36250         this.proxy.update(clone);
36251         return true;
36252     },
36253
36254     afterValidDrop : function(){
36255         var v = this.view;
36256         setTimeout(function(){
36257             v.headersDisabled = false;
36258         }, 50);
36259     },
36260
36261     afterInvalidDrop : function(){
36262         var v = this.view;
36263         setTimeout(function(){
36264             v.headersDisabled = false;
36265         }, 50);
36266     }
36267 });
36268 /*
36269  * Based on:
36270  * Ext JS Library 1.1.1
36271  * Copyright(c) 2006-2007, Ext JS, LLC.
36272  *
36273  * Originally Released Under LGPL - original licence link has changed is not relivant.
36274  *
36275  * Fork - LGPL
36276  * <script type="text/javascript">
36277  */
36278 // private
36279 // This is a support class used internally by the Grid components
36280 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36281     this.grid = grid;
36282     this.view = grid.getView();
36283     // split the proxies so they don't interfere with mouse events
36284     this.proxyTop = Roo.DomHelper.append(document.body, {
36285         cls:"col-move-top", html:"&#160;"
36286     }, true);
36287     this.proxyBottom = Roo.DomHelper.append(document.body, {
36288         cls:"col-move-bottom", html:"&#160;"
36289     }, true);
36290     this.proxyTop.hide = this.proxyBottom.hide = function(){
36291         this.setLeftTop(-100,-100);
36292         this.setStyle("visibility", "hidden");
36293     };
36294     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36295     // temporarily disabled
36296     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36297     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36298 };
36299 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36300     proxyOffsets : [-4, -9],
36301     fly: Roo.Element.fly,
36302
36303     getTargetFromEvent : function(e){
36304         var t = Roo.lib.Event.getTarget(e);
36305         var cindex = this.view.findCellIndex(t);
36306         if(cindex !== false){
36307             return this.view.getHeaderCell(cindex);
36308         }
36309         return null;
36310     },
36311
36312     nextVisible : function(h){
36313         var v = this.view, cm = this.grid.colModel;
36314         h = h.nextSibling;
36315         while(h){
36316             if(!cm.isHidden(v.getCellIndex(h))){
36317                 return h;
36318             }
36319             h = h.nextSibling;
36320         }
36321         return null;
36322     },
36323
36324     prevVisible : function(h){
36325         var v = this.view, cm = this.grid.colModel;
36326         h = h.prevSibling;
36327         while(h){
36328             if(!cm.isHidden(v.getCellIndex(h))){
36329                 return h;
36330             }
36331             h = h.prevSibling;
36332         }
36333         return null;
36334     },
36335
36336     positionIndicator : function(h, n, e){
36337         var x = Roo.lib.Event.getPageX(e);
36338         var r = Roo.lib.Dom.getRegion(n.firstChild);
36339         var px, pt, py = r.top + this.proxyOffsets[1];
36340         if((r.right - x) <= (r.right-r.left)/2){
36341             px = r.right+this.view.borderWidth;
36342             pt = "after";
36343         }else{
36344             px = r.left;
36345             pt = "before";
36346         }
36347         var oldIndex = this.view.getCellIndex(h);
36348         var newIndex = this.view.getCellIndex(n);
36349
36350         if(this.grid.colModel.isFixed(newIndex)){
36351             return false;
36352         }
36353
36354         var locked = this.grid.colModel.isLocked(newIndex);
36355
36356         if(pt == "after"){
36357             newIndex++;
36358         }
36359         if(oldIndex < newIndex){
36360             newIndex--;
36361         }
36362         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36363             return false;
36364         }
36365         px +=  this.proxyOffsets[0];
36366         this.proxyTop.setLeftTop(px, py);
36367         this.proxyTop.show();
36368         if(!this.bottomOffset){
36369             this.bottomOffset = this.view.mainHd.getHeight();
36370         }
36371         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36372         this.proxyBottom.show();
36373         return pt;
36374     },
36375
36376     onNodeEnter : function(n, dd, e, data){
36377         if(data.header != n){
36378             this.positionIndicator(data.header, n, e);
36379         }
36380     },
36381
36382     onNodeOver : function(n, dd, e, data){
36383         var result = false;
36384         if(data.header != n){
36385             result = this.positionIndicator(data.header, n, e);
36386         }
36387         if(!result){
36388             this.proxyTop.hide();
36389             this.proxyBottom.hide();
36390         }
36391         return result ? this.dropAllowed : this.dropNotAllowed;
36392     },
36393
36394     onNodeOut : function(n, dd, e, data){
36395         this.proxyTop.hide();
36396         this.proxyBottom.hide();
36397     },
36398
36399     onNodeDrop : function(n, dd, e, data){
36400         var h = data.header;
36401         if(h != n){
36402             var cm = this.grid.colModel;
36403             var x = Roo.lib.Event.getPageX(e);
36404             var r = Roo.lib.Dom.getRegion(n.firstChild);
36405             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36406             var oldIndex = this.view.getCellIndex(h);
36407             var newIndex = this.view.getCellIndex(n);
36408             var locked = cm.isLocked(newIndex);
36409             if(pt == "after"){
36410                 newIndex++;
36411             }
36412             if(oldIndex < newIndex){
36413                 newIndex--;
36414             }
36415             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36416                 return false;
36417             }
36418             cm.setLocked(oldIndex, locked, true);
36419             cm.moveColumn(oldIndex, newIndex);
36420             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36421             return true;
36422         }
36423         return false;
36424     }
36425 });
36426 /*
36427  * Based on:
36428  * Ext JS Library 1.1.1
36429  * Copyright(c) 2006-2007, Ext JS, LLC.
36430  *
36431  * Originally Released Under LGPL - original licence link has changed is not relivant.
36432  *
36433  * Fork - LGPL
36434  * <script type="text/javascript">
36435  */
36436   
36437 /**
36438  * @class Roo.grid.GridView
36439  * @extends Roo.util.Observable
36440  *
36441  * @constructor
36442  * @param {Object} config
36443  */
36444 Roo.grid.GridView = function(config){
36445     Roo.grid.GridView.superclass.constructor.call(this);
36446     this.el = null;
36447
36448     Roo.apply(this, config);
36449 };
36450
36451 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36452
36453     unselectable :  'unselectable="on"',
36454     unselectableCls :  'x-unselectable',
36455     
36456     
36457     rowClass : "x-grid-row",
36458
36459     cellClass : "x-grid-col",
36460
36461     tdClass : "x-grid-td",
36462
36463     hdClass : "x-grid-hd",
36464
36465     splitClass : "x-grid-split",
36466
36467     sortClasses : ["sort-asc", "sort-desc"],
36468
36469     enableMoveAnim : false,
36470
36471     hlColor: "C3DAF9",
36472
36473     dh : Roo.DomHelper,
36474
36475     fly : Roo.Element.fly,
36476
36477     css : Roo.util.CSS,
36478
36479     borderWidth: 1,
36480
36481     splitOffset: 3,
36482
36483     scrollIncrement : 22,
36484
36485     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36486
36487     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36488
36489     bind : function(ds, cm){
36490         if(this.ds){
36491             this.ds.un("load", this.onLoad, this);
36492             this.ds.un("datachanged", this.onDataChange, this);
36493             this.ds.un("add", this.onAdd, this);
36494             this.ds.un("remove", this.onRemove, this);
36495             this.ds.un("update", this.onUpdate, this);
36496             this.ds.un("clear", this.onClear, this);
36497         }
36498         if(ds){
36499             ds.on("load", this.onLoad, this);
36500             ds.on("datachanged", this.onDataChange, this);
36501             ds.on("add", this.onAdd, this);
36502             ds.on("remove", this.onRemove, this);
36503             ds.on("update", this.onUpdate, this);
36504             ds.on("clear", this.onClear, this);
36505         }
36506         this.ds = ds;
36507
36508         if(this.cm){
36509             this.cm.un("widthchange", this.onColWidthChange, this);
36510             this.cm.un("headerchange", this.onHeaderChange, this);
36511             this.cm.un("hiddenchange", this.onHiddenChange, this);
36512             this.cm.un("columnmoved", this.onColumnMove, this);
36513             this.cm.un("columnlockchange", this.onColumnLock, this);
36514         }
36515         if(cm){
36516             this.generateRules(cm);
36517             cm.on("widthchange", this.onColWidthChange, this);
36518             cm.on("headerchange", this.onHeaderChange, this);
36519             cm.on("hiddenchange", this.onHiddenChange, this);
36520             cm.on("columnmoved", this.onColumnMove, this);
36521             cm.on("columnlockchange", this.onColumnLock, this);
36522         }
36523         this.cm = cm;
36524     },
36525
36526     init: function(grid){
36527         Roo.grid.GridView.superclass.init.call(this, grid);
36528
36529         this.bind(grid.dataSource, grid.colModel);
36530
36531         grid.on("headerclick", this.handleHeaderClick, this);
36532
36533         if(grid.trackMouseOver){
36534             grid.on("mouseover", this.onRowOver, this);
36535             grid.on("mouseout", this.onRowOut, this);
36536         }
36537         grid.cancelTextSelection = function(){};
36538         this.gridId = grid.id;
36539
36540         var tpls = this.templates || {};
36541
36542         if(!tpls.master){
36543             tpls.master = new Roo.Template(
36544                '<div class="x-grid" hidefocus="true">',
36545                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36546                   '<div class="x-grid-topbar"></div>',
36547                   '<div class="x-grid-scroller"><div></div></div>',
36548                   '<div class="x-grid-locked">',
36549                       '<div class="x-grid-header">{lockedHeader}</div>',
36550                       '<div class="x-grid-body">{lockedBody}</div>',
36551                   "</div>",
36552                   '<div class="x-grid-viewport">',
36553                       '<div class="x-grid-header">{header}</div>',
36554                       '<div class="x-grid-body">{body}</div>',
36555                   "</div>",
36556                   '<div class="x-grid-bottombar"></div>',
36557                  
36558                   '<div class="x-grid-resize-proxy">&#160;</div>',
36559                "</div>"
36560             );
36561             tpls.master.disableformats = true;
36562         }
36563
36564         if(!tpls.header){
36565             tpls.header = new Roo.Template(
36566                '<table border="0" cellspacing="0" cellpadding="0">',
36567                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36568                "</table>{splits}"
36569             );
36570             tpls.header.disableformats = true;
36571         }
36572         tpls.header.compile();
36573
36574         if(!tpls.hcell){
36575             tpls.hcell = new Roo.Template(
36576                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36577                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36578                 "</div></td>"
36579              );
36580              tpls.hcell.disableFormats = true;
36581         }
36582         tpls.hcell.compile();
36583
36584         if(!tpls.hsplit){
36585             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36586                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36587             tpls.hsplit.disableFormats = true;
36588         }
36589         tpls.hsplit.compile();
36590
36591         if(!tpls.body){
36592             tpls.body = new Roo.Template(
36593                '<table border="0" cellspacing="0" cellpadding="0">',
36594                "<tbody>{rows}</tbody>",
36595                "</table>"
36596             );
36597             tpls.body.disableFormats = true;
36598         }
36599         tpls.body.compile();
36600
36601         if(!tpls.row){
36602             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36603             tpls.row.disableFormats = true;
36604         }
36605         tpls.row.compile();
36606
36607         if(!tpls.cell){
36608             tpls.cell = new Roo.Template(
36609                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36610                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36611                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36612                 "</td>"
36613             );
36614             tpls.cell.disableFormats = true;
36615         }
36616         tpls.cell.compile();
36617
36618         this.templates = tpls;
36619     },
36620
36621     // remap these for backwards compat
36622     onColWidthChange : function(){
36623         this.updateColumns.apply(this, arguments);
36624     },
36625     onHeaderChange : function(){
36626         this.updateHeaders.apply(this, arguments);
36627     }, 
36628     onHiddenChange : function(){
36629         this.handleHiddenChange.apply(this, arguments);
36630     },
36631     onColumnMove : function(){
36632         this.handleColumnMove.apply(this, arguments);
36633     },
36634     onColumnLock : function(){
36635         this.handleLockChange.apply(this, arguments);
36636     },
36637
36638     onDataChange : function(){
36639         this.refresh();
36640         this.updateHeaderSortState();
36641     },
36642
36643     onClear : function(){
36644         this.refresh();
36645     },
36646
36647     onUpdate : function(ds, record){
36648         this.refreshRow(record);
36649     },
36650
36651     refreshRow : function(record){
36652         var ds = this.ds, index;
36653         if(typeof record == 'number'){
36654             index = record;
36655             record = ds.getAt(index);
36656         }else{
36657             index = ds.indexOf(record);
36658         }
36659         this.insertRows(ds, index, index, true);
36660         this.onRemove(ds, record, index+1, true);
36661         this.syncRowHeights(index, index);
36662         this.layout();
36663         this.fireEvent("rowupdated", this, index, record);
36664     },
36665
36666     onAdd : function(ds, records, index){
36667         this.insertRows(ds, index, index + (records.length-1));
36668     },
36669
36670     onRemove : function(ds, record, index, isUpdate){
36671         if(isUpdate !== true){
36672             this.fireEvent("beforerowremoved", this, index, record);
36673         }
36674         var bt = this.getBodyTable(), lt = this.getLockedTable();
36675         if(bt.rows[index]){
36676             bt.firstChild.removeChild(bt.rows[index]);
36677         }
36678         if(lt.rows[index]){
36679             lt.firstChild.removeChild(lt.rows[index]);
36680         }
36681         if(isUpdate !== true){
36682             this.stripeRows(index);
36683             this.syncRowHeights(index, index);
36684             this.layout();
36685             this.fireEvent("rowremoved", this, index, record);
36686         }
36687     },
36688
36689     onLoad : function(){
36690         this.scrollToTop();
36691     },
36692
36693     /**
36694      * Scrolls the grid to the top
36695      */
36696     scrollToTop : function(){
36697         if(this.scroller){
36698             this.scroller.dom.scrollTop = 0;
36699             this.syncScroll();
36700         }
36701     },
36702
36703     /**
36704      * Gets a panel in the header of the grid that can be used for toolbars etc.
36705      * After modifying the contents of this panel a call to grid.autoSize() may be
36706      * required to register any changes in size.
36707      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36708      * @return Roo.Element
36709      */
36710     getHeaderPanel : function(doShow){
36711         if(doShow){
36712             this.headerPanel.show();
36713         }
36714         return this.headerPanel;
36715     },
36716
36717     /**
36718      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36719      * After modifying the contents of this panel a call to grid.autoSize() may be
36720      * required to register any changes in size.
36721      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36722      * @return Roo.Element
36723      */
36724     getFooterPanel : function(doShow){
36725         if(doShow){
36726             this.footerPanel.show();
36727         }
36728         return this.footerPanel;
36729     },
36730
36731     initElements : function(){
36732         var E = Roo.Element;
36733         var el = this.grid.getGridEl().dom.firstChild;
36734         var cs = el.childNodes;
36735
36736         this.el = new E(el);
36737         
36738          this.focusEl = new E(el.firstChild);
36739         this.focusEl.swallowEvent("click", true);
36740         
36741         this.headerPanel = new E(cs[1]);
36742         this.headerPanel.enableDisplayMode("block");
36743
36744         this.scroller = new E(cs[2]);
36745         this.scrollSizer = new E(this.scroller.dom.firstChild);
36746
36747         this.lockedWrap = new E(cs[3]);
36748         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36749         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36750
36751         this.mainWrap = new E(cs[4]);
36752         this.mainHd = new E(this.mainWrap.dom.firstChild);
36753         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36754
36755         this.footerPanel = new E(cs[5]);
36756         this.footerPanel.enableDisplayMode("block");
36757
36758         this.resizeProxy = new E(cs[6]);
36759
36760         this.headerSelector = String.format(
36761            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36762            this.lockedHd.id, this.mainHd.id
36763         );
36764
36765         this.splitterSelector = String.format(
36766            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36767            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36768         );
36769     },
36770     idToCssName : function(s)
36771     {
36772         return s.replace(/[^a-z0-9]+/ig, '-');
36773     },
36774
36775     getHeaderCell : function(index){
36776         return Roo.DomQuery.select(this.headerSelector)[index];
36777     },
36778
36779     getHeaderCellMeasure : function(index){
36780         return this.getHeaderCell(index).firstChild;
36781     },
36782
36783     getHeaderCellText : function(index){
36784         return this.getHeaderCell(index).firstChild.firstChild;
36785     },
36786
36787     getLockedTable : function(){
36788         return this.lockedBody.dom.firstChild;
36789     },
36790
36791     getBodyTable : function(){
36792         return this.mainBody.dom.firstChild;
36793     },
36794
36795     getLockedRow : function(index){
36796         return this.getLockedTable().rows[index];
36797     },
36798
36799     getRow : function(index){
36800         return this.getBodyTable().rows[index];
36801     },
36802
36803     getRowComposite : function(index){
36804         if(!this.rowEl){
36805             this.rowEl = new Roo.CompositeElementLite();
36806         }
36807         var els = [], lrow, mrow;
36808         if(lrow = this.getLockedRow(index)){
36809             els.push(lrow);
36810         }
36811         if(mrow = this.getRow(index)){
36812             els.push(mrow);
36813         }
36814         this.rowEl.elements = els;
36815         return this.rowEl;
36816     },
36817     /**
36818      * Gets the 'td' of the cell
36819      * 
36820      * @param {Integer} rowIndex row to select
36821      * @param {Integer} colIndex column to select
36822      * 
36823      * @return {Object} 
36824      */
36825     getCell : function(rowIndex, colIndex){
36826         var locked = this.cm.getLockedCount();
36827         var source;
36828         if(colIndex < locked){
36829             source = this.lockedBody.dom.firstChild;
36830         }else{
36831             source = this.mainBody.dom.firstChild;
36832             colIndex -= locked;
36833         }
36834         return source.rows[rowIndex].childNodes[colIndex];
36835     },
36836
36837     getCellText : function(rowIndex, colIndex){
36838         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36839     },
36840
36841     getCellBox : function(cell){
36842         var b = this.fly(cell).getBox();
36843         if(Roo.isOpera){ // opera fails to report the Y
36844             b.y = cell.offsetTop + this.mainBody.getY();
36845         }
36846         return b;
36847     },
36848
36849     getCellIndex : function(cell){
36850         var id = String(cell.className).match(this.cellRE);
36851         if(id){
36852             return parseInt(id[1], 10);
36853         }
36854         return 0;
36855     },
36856
36857     findHeaderIndex : function(n){
36858         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36859         return r ? this.getCellIndex(r) : false;
36860     },
36861
36862     findHeaderCell : function(n){
36863         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36864         return r ? r : false;
36865     },
36866
36867     findRowIndex : function(n){
36868         if(!n){
36869             return false;
36870         }
36871         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36872         return r ? r.rowIndex : false;
36873     },
36874
36875     findCellIndex : function(node){
36876         var stop = this.el.dom;
36877         while(node && node != stop){
36878             if(this.findRE.test(node.className)){
36879                 return this.getCellIndex(node);
36880             }
36881             node = node.parentNode;
36882         }
36883         return false;
36884     },
36885
36886     getColumnId : function(index){
36887         return this.cm.getColumnId(index);
36888     },
36889
36890     getSplitters : function()
36891     {
36892         if(this.splitterSelector){
36893            return Roo.DomQuery.select(this.splitterSelector);
36894         }else{
36895             return null;
36896       }
36897     },
36898
36899     getSplitter : function(index){
36900         return this.getSplitters()[index];
36901     },
36902
36903     onRowOver : function(e, t){
36904         var row;
36905         if((row = this.findRowIndex(t)) !== false){
36906             this.getRowComposite(row).addClass("x-grid-row-over");
36907         }
36908     },
36909
36910     onRowOut : function(e, t){
36911         var row;
36912         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36913             this.getRowComposite(row).removeClass("x-grid-row-over");
36914         }
36915     },
36916
36917     renderHeaders : function(){
36918         var cm = this.cm;
36919         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36920         var cb = [], lb = [], sb = [], lsb = [], p = {};
36921         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36922             p.cellId = "x-grid-hd-0-" + i;
36923             p.splitId = "x-grid-csplit-0-" + i;
36924             p.id = cm.getColumnId(i);
36925             p.title = cm.getColumnTooltip(i) || "";
36926             p.value = cm.getColumnHeader(i) || "";
36927             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36928             if(!cm.isLocked(i)){
36929                 cb[cb.length] = ct.apply(p);
36930                 sb[sb.length] = st.apply(p);
36931             }else{
36932                 lb[lb.length] = ct.apply(p);
36933                 lsb[lsb.length] = st.apply(p);
36934             }
36935         }
36936         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36937                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36938     },
36939
36940     updateHeaders : function(){
36941         var html = this.renderHeaders();
36942         this.lockedHd.update(html[0]);
36943         this.mainHd.update(html[1]);
36944     },
36945
36946     /**
36947      * Focuses the specified row.
36948      * @param {Number} row The row index
36949      */
36950     focusRow : function(row)
36951     {
36952         //Roo.log('GridView.focusRow');
36953         var x = this.scroller.dom.scrollLeft;
36954         this.focusCell(row, 0, false);
36955         this.scroller.dom.scrollLeft = x;
36956     },
36957
36958     /**
36959      * Focuses the specified cell.
36960      * @param {Number} row The row index
36961      * @param {Number} col The column index
36962      * @param {Boolean} hscroll false to disable horizontal scrolling
36963      */
36964     focusCell : function(row, col, hscroll)
36965     {
36966         //Roo.log('GridView.focusCell');
36967         var el = this.ensureVisible(row, col, hscroll);
36968         this.focusEl.alignTo(el, "tl-tl");
36969         if(Roo.isGecko){
36970             this.focusEl.focus();
36971         }else{
36972             this.focusEl.focus.defer(1, this.focusEl);
36973         }
36974     },
36975
36976     /**
36977      * Scrolls the specified cell into view
36978      * @param {Number} row The row index
36979      * @param {Number} col The column index
36980      * @param {Boolean} hscroll false to disable horizontal scrolling
36981      */
36982     ensureVisible : function(row, col, hscroll)
36983     {
36984         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36985         //return null; //disable for testing.
36986         if(typeof row != "number"){
36987             row = row.rowIndex;
36988         }
36989         if(row < 0 && row >= this.ds.getCount()){
36990             return  null;
36991         }
36992         col = (col !== undefined ? col : 0);
36993         var cm = this.grid.colModel;
36994         while(cm.isHidden(col)){
36995             col++;
36996         }
36997
36998         var el = this.getCell(row, col);
36999         if(!el){
37000             return null;
37001         }
37002         var c = this.scroller.dom;
37003
37004         var ctop = parseInt(el.offsetTop, 10);
37005         var cleft = parseInt(el.offsetLeft, 10);
37006         var cbot = ctop + el.offsetHeight;
37007         var cright = cleft + el.offsetWidth;
37008         
37009         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37010         var stop = parseInt(c.scrollTop, 10);
37011         var sleft = parseInt(c.scrollLeft, 10);
37012         var sbot = stop + ch;
37013         var sright = sleft + c.clientWidth;
37014         /*
37015         Roo.log('GridView.ensureVisible:' +
37016                 ' ctop:' + ctop +
37017                 ' c.clientHeight:' + c.clientHeight +
37018                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37019                 ' stop:' + stop +
37020                 ' cbot:' + cbot +
37021                 ' sbot:' + sbot +
37022                 ' ch:' + ch  
37023                 );
37024         */
37025         if(ctop < stop){
37026              c.scrollTop = ctop;
37027             //Roo.log("set scrolltop to ctop DISABLE?");
37028         }else if(cbot > sbot){
37029             //Roo.log("set scrolltop to cbot-ch");
37030             c.scrollTop = cbot-ch;
37031         }
37032         
37033         if(hscroll !== false){
37034             if(cleft < sleft){
37035                 c.scrollLeft = cleft;
37036             }else if(cright > sright){
37037                 c.scrollLeft = cright-c.clientWidth;
37038             }
37039         }
37040          
37041         return el;
37042     },
37043
37044     updateColumns : function(){
37045         this.grid.stopEditing();
37046         var cm = this.grid.colModel, colIds = this.getColumnIds();
37047         //var totalWidth = cm.getTotalWidth();
37048         var pos = 0;
37049         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37050             //if(cm.isHidden(i)) continue;
37051             var w = cm.getColumnWidth(i);
37052             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37053             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37054         }
37055         this.updateSplitters();
37056     },
37057
37058     generateRules : function(cm){
37059         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37060         Roo.util.CSS.removeStyleSheet(rulesId);
37061         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37062             var cid = cm.getColumnId(i);
37063             var align = '';
37064             if(cm.config[i].align){
37065                 align = 'text-align:'+cm.config[i].align+';';
37066             }
37067             var hidden = '';
37068             if(cm.isHidden(i)){
37069                 hidden = 'display:none;';
37070             }
37071             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37072             ruleBuf.push(
37073                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37074                     this.hdSelector, cid, " {\n", align, width, "}\n",
37075                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37076                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37077         }
37078         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37079     },
37080
37081     updateSplitters : function(){
37082         var cm = this.cm, s = this.getSplitters();
37083         if(s){ // splitters not created yet
37084             var pos = 0, locked = true;
37085             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37086                 if(cm.isHidden(i)) continue;
37087                 var w = cm.getColumnWidth(i); // make sure it's a number
37088                 if(!cm.isLocked(i) && locked){
37089                     pos = 0;
37090                     locked = false;
37091                 }
37092                 pos += w;
37093                 s[i].style.left = (pos-this.splitOffset) + "px";
37094             }
37095         }
37096     },
37097
37098     handleHiddenChange : function(colModel, colIndex, hidden){
37099         if(hidden){
37100             this.hideColumn(colIndex);
37101         }else{
37102             this.unhideColumn(colIndex);
37103         }
37104     },
37105
37106     hideColumn : function(colIndex){
37107         var cid = this.getColumnId(colIndex);
37108         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37109         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37110         if(Roo.isSafari){
37111             this.updateHeaders();
37112         }
37113         this.updateSplitters();
37114         this.layout();
37115     },
37116
37117     unhideColumn : function(colIndex){
37118         var cid = this.getColumnId(colIndex);
37119         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37120         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37121
37122         if(Roo.isSafari){
37123             this.updateHeaders();
37124         }
37125         this.updateSplitters();
37126         this.layout();
37127     },
37128
37129     insertRows : function(dm, firstRow, lastRow, isUpdate){
37130         if(firstRow == 0 && lastRow == dm.getCount()-1){
37131             this.refresh();
37132         }else{
37133             if(!isUpdate){
37134                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37135             }
37136             var s = this.getScrollState();
37137             var markup = this.renderRows(firstRow, lastRow);
37138             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37139             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37140             this.restoreScroll(s);
37141             if(!isUpdate){
37142                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37143                 this.syncRowHeights(firstRow, lastRow);
37144                 this.stripeRows(firstRow);
37145                 this.layout();
37146             }
37147         }
37148     },
37149
37150     bufferRows : function(markup, target, index){
37151         var before = null, trows = target.rows, tbody = target.tBodies[0];
37152         if(index < trows.length){
37153             before = trows[index];
37154         }
37155         var b = document.createElement("div");
37156         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37157         var rows = b.firstChild.rows;
37158         for(var i = 0, len = rows.length; i < len; i++){
37159             if(before){
37160                 tbody.insertBefore(rows[0], before);
37161             }else{
37162                 tbody.appendChild(rows[0]);
37163             }
37164         }
37165         b.innerHTML = "";
37166         b = null;
37167     },
37168
37169     deleteRows : function(dm, firstRow, lastRow){
37170         if(dm.getRowCount()<1){
37171             this.fireEvent("beforerefresh", this);
37172             this.mainBody.update("");
37173             this.lockedBody.update("");
37174             this.fireEvent("refresh", this);
37175         }else{
37176             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37177             var bt = this.getBodyTable();
37178             var tbody = bt.firstChild;
37179             var rows = bt.rows;
37180             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37181                 tbody.removeChild(rows[firstRow]);
37182             }
37183             this.stripeRows(firstRow);
37184             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37185         }
37186     },
37187
37188     updateRows : function(dataSource, firstRow, lastRow){
37189         var s = this.getScrollState();
37190         this.refresh();
37191         this.restoreScroll(s);
37192     },
37193
37194     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37195         if(!noRefresh){
37196            this.refresh();
37197         }
37198         this.updateHeaderSortState();
37199     },
37200
37201     getScrollState : function(){
37202         
37203         var sb = this.scroller.dom;
37204         return {left: sb.scrollLeft, top: sb.scrollTop};
37205     },
37206
37207     stripeRows : function(startRow){
37208         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37209             return;
37210         }
37211         startRow = startRow || 0;
37212         var rows = this.getBodyTable().rows;
37213         var lrows = this.getLockedTable().rows;
37214         var cls = ' x-grid-row-alt ';
37215         for(var i = startRow, len = rows.length; i < len; i++){
37216             var row = rows[i], lrow = lrows[i];
37217             var isAlt = ((i+1) % 2 == 0);
37218             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37219             if(isAlt == hasAlt){
37220                 continue;
37221             }
37222             if(isAlt){
37223                 row.className += " x-grid-row-alt";
37224             }else{
37225                 row.className = row.className.replace("x-grid-row-alt", "");
37226             }
37227             if(lrow){
37228                 lrow.className = row.className;
37229             }
37230         }
37231     },
37232
37233     restoreScroll : function(state){
37234         //Roo.log('GridView.restoreScroll');
37235         var sb = this.scroller.dom;
37236         sb.scrollLeft = state.left;
37237         sb.scrollTop = state.top;
37238         this.syncScroll();
37239     },
37240
37241     syncScroll : function(){
37242         //Roo.log('GridView.syncScroll');
37243         var sb = this.scroller.dom;
37244         var sh = this.mainHd.dom;
37245         var bs = this.mainBody.dom;
37246         var lv = this.lockedBody.dom;
37247         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37248         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37249     },
37250
37251     handleScroll : function(e){
37252         this.syncScroll();
37253         var sb = this.scroller.dom;
37254         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37255         e.stopEvent();
37256     },
37257
37258     handleWheel : function(e){
37259         var d = e.getWheelDelta();
37260         this.scroller.dom.scrollTop -= d*22;
37261         // set this here to prevent jumpy scrolling on large tables
37262         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37263         e.stopEvent();
37264     },
37265
37266     renderRows : function(startRow, endRow){
37267         // pull in all the crap needed to render rows
37268         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37269         var colCount = cm.getColumnCount();
37270
37271         if(ds.getCount() < 1){
37272             return ["", ""];
37273         }
37274
37275         // build a map for all the columns
37276         var cs = [];
37277         for(var i = 0; i < colCount; i++){
37278             var name = cm.getDataIndex(i);
37279             cs[i] = {
37280                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37281                 renderer : cm.getRenderer(i),
37282                 id : cm.getColumnId(i),
37283                 locked : cm.isLocked(i)
37284             };
37285         }
37286
37287         startRow = startRow || 0;
37288         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37289
37290         // records to render
37291         var rs = ds.getRange(startRow, endRow);
37292
37293         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37294     },
37295
37296     // As much as I hate to duplicate code, this was branched because FireFox really hates
37297     // [].join("") on strings. The performance difference was substantial enough to
37298     // branch this function
37299     doRender : Roo.isGecko ?
37300             function(cs, rs, ds, startRow, colCount, stripe){
37301                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37302                 // buffers
37303                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37304                 
37305                 var hasListener = this.grid.hasListener('rowclass');
37306                 var rowcfg = {};
37307                 for(var j = 0, len = rs.length; j < len; j++){
37308                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37309                     for(var i = 0; i < colCount; i++){
37310                         c = cs[i];
37311                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37312                         p.id = c.id;
37313                         p.css = p.attr = "";
37314                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37315                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37316                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37317                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37318                         }
37319                         var markup = ct.apply(p);
37320                         if(!c.locked){
37321                             cb+= markup;
37322                         }else{
37323                             lcb+= markup;
37324                         }
37325                     }
37326                     var alt = [];
37327                     if(stripe && ((rowIndex+1) % 2 == 0)){
37328                         alt.push("x-grid-row-alt")
37329                     }
37330                     if(r.dirty){
37331                         alt.push(  " x-grid-dirty-row");
37332                     }
37333                     rp.cells = lcb;
37334                     if(this.getRowClass){
37335                         alt.push(this.getRowClass(r, rowIndex));
37336                     }
37337                     if (hasListener) {
37338                         rowcfg = {
37339                              
37340                             record: r,
37341                             rowIndex : rowIndex,
37342                             rowClass : ''
37343                         }
37344                         this.grid.fireEvent('rowclass', this, rowcfg);
37345                         alt.push(rowcfg.rowClass);
37346                     }
37347                     rp.alt = alt.join(" ");
37348                     lbuf+= rt.apply(rp);
37349                     rp.cells = cb;
37350                     buf+=  rt.apply(rp);
37351                 }
37352                 return [lbuf, buf];
37353             } :
37354             function(cs, rs, ds, startRow, colCount, stripe){
37355                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37356                 // buffers
37357                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37358                 var hasListener = this.grid.hasListener('rowclass');
37359  
37360                 var rowcfg = {};
37361                 for(var j = 0, len = rs.length; j < len; j++){
37362                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37363                     for(var i = 0; i < colCount; i++){
37364                         c = cs[i];
37365                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37366                         p.id = c.id;
37367                         p.css = p.attr = "";
37368                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37369                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37370                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37371                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37372                         }
37373                         
37374                         var markup = ct.apply(p);
37375                         if(!c.locked){
37376                             cb[cb.length] = markup;
37377                         }else{
37378                             lcb[lcb.length] = markup;
37379                         }
37380                     }
37381                     var alt = [];
37382                     if(stripe && ((rowIndex+1) % 2 == 0)){
37383                         alt.push( "x-grid-row-alt");
37384                     }
37385                     if(r.dirty){
37386                         alt.push(" x-grid-dirty-row");
37387                     }
37388                     rp.cells = lcb;
37389                     if(this.getRowClass){
37390                         alt.push( this.getRowClass(r, rowIndex));
37391                     }
37392                     if (hasListener) {
37393                         rowcfg = {
37394                              
37395                             record: r,
37396                             rowIndex : rowIndex,
37397                             rowClass : ''
37398                         }
37399                         this.grid.fireEvent('rowclass', this, rowcfg);
37400                         alt.push(rowcfg.rowClass);
37401                     }
37402                     rp.alt = alt.join(" ");
37403                     rp.cells = lcb.join("");
37404                     lbuf[lbuf.length] = rt.apply(rp);
37405                     rp.cells = cb.join("");
37406                     buf[buf.length] =  rt.apply(rp);
37407                 }
37408                 return [lbuf.join(""), buf.join("")];
37409             },
37410
37411     renderBody : function(){
37412         var markup = this.renderRows();
37413         var bt = this.templates.body;
37414         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37415     },
37416
37417     /**
37418      * Refreshes the grid
37419      * @param {Boolean} headersToo
37420      */
37421     refresh : function(headersToo){
37422         this.fireEvent("beforerefresh", this);
37423         this.grid.stopEditing();
37424         var result = this.renderBody();
37425         this.lockedBody.update(result[0]);
37426         this.mainBody.update(result[1]);
37427         if(headersToo === true){
37428             this.updateHeaders();
37429             this.updateColumns();
37430             this.updateSplitters();
37431             this.updateHeaderSortState();
37432         }
37433         this.syncRowHeights();
37434         this.layout();
37435         this.fireEvent("refresh", this);
37436     },
37437
37438     handleColumnMove : function(cm, oldIndex, newIndex){
37439         this.indexMap = null;
37440         var s = this.getScrollState();
37441         this.refresh(true);
37442         this.restoreScroll(s);
37443         this.afterMove(newIndex);
37444     },
37445
37446     afterMove : function(colIndex){
37447         if(this.enableMoveAnim && Roo.enableFx){
37448             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37449         }
37450         // if multisort - fix sortOrder, and reload..
37451         if (this.grid.dataSource.multiSort) {
37452             // the we can call sort again..
37453             var dm = this.grid.dataSource;
37454             var cm = this.grid.colModel;
37455             var so = [];
37456             for(var i = 0; i < cm.config.length; i++ ) {
37457                 
37458                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37459                     continue; // dont' bother, it's not in sort list or being set.
37460                 }
37461                 
37462                 so.push(cm.config[i].dataIndex);
37463             };
37464             dm.sortOrder = so;
37465             dm.load(dm.lastOptions);
37466             
37467             
37468         }
37469         
37470     },
37471
37472     updateCell : function(dm, rowIndex, dataIndex){
37473         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37474         if(typeof colIndex == "undefined"){ // not present in grid
37475             return;
37476         }
37477         var cm = this.grid.colModel;
37478         var cell = this.getCell(rowIndex, colIndex);
37479         var cellText = this.getCellText(rowIndex, colIndex);
37480
37481         var p = {
37482             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37483             id : cm.getColumnId(colIndex),
37484             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37485         };
37486         var renderer = cm.getRenderer(colIndex);
37487         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37488         if(typeof val == "undefined" || val === "") val = "&#160;";
37489         cellText.innerHTML = val;
37490         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37491         this.syncRowHeights(rowIndex, rowIndex);
37492     },
37493
37494     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37495         var maxWidth = 0;
37496         if(this.grid.autoSizeHeaders){
37497             var h = this.getHeaderCellMeasure(colIndex);
37498             maxWidth = Math.max(maxWidth, h.scrollWidth);
37499         }
37500         var tb, index;
37501         if(this.cm.isLocked(colIndex)){
37502             tb = this.getLockedTable();
37503             index = colIndex;
37504         }else{
37505             tb = this.getBodyTable();
37506             index = colIndex - this.cm.getLockedCount();
37507         }
37508         if(tb && tb.rows){
37509             var rows = tb.rows;
37510             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37511             for(var i = 0; i < stopIndex; i++){
37512                 var cell = rows[i].childNodes[index].firstChild;
37513                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37514             }
37515         }
37516         return maxWidth + /*margin for error in IE*/ 5;
37517     },
37518     /**
37519      * Autofit a column to its content.
37520      * @param {Number} colIndex
37521      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37522      */
37523      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37524          if(this.cm.isHidden(colIndex)){
37525              return; // can't calc a hidden column
37526          }
37527         if(forceMinSize){
37528             var cid = this.cm.getColumnId(colIndex);
37529             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37530            if(this.grid.autoSizeHeaders){
37531                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37532            }
37533         }
37534         var newWidth = this.calcColumnWidth(colIndex);
37535         this.cm.setColumnWidth(colIndex,
37536             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37537         if(!suppressEvent){
37538             this.grid.fireEvent("columnresize", colIndex, newWidth);
37539         }
37540     },
37541
37542     /**
37543      * Autofits all columns to their content and then expands to fit any extra space in the grid
37544      */
37545      autoSizeColumns : function(){
37546         var cm = this.grid.colModel;
37547         var colCount = cm.getColumnCount();
37548         for(var i = 0; i < colCount; i++){
37549             this.autoSizeColumn(i, true, true);
37550         }
37551         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37552             this.fitColumns();
37553         }else{
37554             this.updateColumns();
37555             this.layout();
37556         }
37557     },
37558
37559     /**
37560      * Autofits all columns to the grid's width proportionate with their current size
37561      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37562      */
37563     fitColumns : function(reserveScrollSpace){
37564         var cm = this.grid.colModel;
37565         var colCount = cm.getColumnCount();
37566         var cols = [];
37567         var width = 0;
37568         var i, w;
37569         for (i = 0; i < colCount; i++){
37570             if(!cm.isHidden(i) && !cm.isFixed(i)){
37571                 w = cm.getColumnWidth(i);
37572                 cols.push(i);
37573                 cols.push(w);
37574                 width += w;
37575             }
37576         }
37577         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37578         if(reserveScrollSpace){
37579             avail -= 17;
37580         }
37581         var frac = (avail - cm.getTotalWidth())/width;
37582         while (cols.length){
37583             w = cols.pop();
37584             i = cols.pop();
37585             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37586         }
37587         this.updateColumns();
37588         this.layout();
37589     },
37590
37591     onRowSelect : function(rowIndex){
37592         var row = this.getRowComposite(rowIndex);
37593         row.addClass("x-grid-row-selected");
37594     },
37595
37596     onRowDeselect : function(rowIndex){
37597         var row = this.getRowComposite(rowIndex);
37598         row.removeClass("x-grid-row-selected");
37599     },
37600
37601     onCellSelect : function(row, col){
37602         var cell = this.getCell(row, col);
37603         if(cell){
37604             Roo.fly(cell).addClass("x-grid-cell-selected");
37605         }
37606     },
37607
37608     onCellDeselect : function(row, col){
37609         var cell = this.getCell(row, col);
37610         if(cell){
37611             Roo.fly(cell).removeClass("x-grid-cell-selected");
37612         }
37613     },
37614
37615     updateHeaderSortState : function(){
37616         
37617         // sort state can be single { field: xxx, direction : yyy}
37618         // or   { xxx=>ASC , yyy : DESC ..... }
37619         
37620         var mstate = {};
37621         if (!this.ds.multiSort) { 
37622             var state = this.ds.getSortState();
37623             if(!state){
37624                 return;
37625             }
37626             mstate[state.field] = state.direction;
37627             // FIXME... - this is not used here.. but might be elsewhere..
37628             this.sortState = state;
37629             
37630         } else {
37631             mstate = this.ds.sortToggle;
37632         }
37633         //remove existing sort classes..
37634         
37635         var sc = this.sortClasses;
37636         var hds = this.el.select(this.headerSelector).removeClass(sc);
37637         
37638         for(var f in mstate) {
37639         
37640             var sortColumn = this.cm.findColumnIndex(f);
37641             
37642             if(sortColumn != -1){
37643                 var sortDir = mstate[f];        
37644                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37645             }
37646         }
37647         
37648          
37649         
37650     },
37651
37652
37653     handleHeaderClick : function(g, index){
37654         if(this.headersDisabled){
37655             return;
37656         }
37657         var dm = g.dataSource, cm = g.colModel;
37658         if(!cm.isSortable(index)){
37659             return;
37660         }
37661         g.stopEditing();
37662         
37663         if (dm.multiSort) {
37664             // update the sortOrder
37665             var so = [];
37666             for(var i = 0; i < cm.config.length; i++ ) {
37667                 
37668                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37669                     continue; // dont' bother, it's not in sort list or being set.
37670                 }
37671                 
37672                 so.push(cm.config[i].dataIndex);
37673             };
37674             dm.sortOrder = so;
37675         }
37676         
37677         
37678         dm.sort(cm.getDataIndex(index));
37679     },
37680
37681
37682     destroy : function(){
37683         if(this.colMenu){
37684             this.colMenu.removeAll();
37685             Roo.menu.MenuMgr.unregister(this.colMenu);
37686             this.colMenu.getEl().remove();
37687             delete this.colMenu;
37688         }
37689         if(this.hmenu){
37690             this.hmenu.removeAll();
37691             Roo.menu.MenuMgr.unregister(this.hmenu);
37692             this.hmenu.getEl().remove();
37693             delete this.hmenu;
37694         }
37695         if(this.grid.enableColumnMove){
37696             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37697             if(dds){
37698                 for(var dd in dds){
37699                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37700                         var elid = dds[dd].dragElId;
37701                         dds[dd].unreg();
37702                         Roo.get(elid).remove();
37703                     } else if(dds[dd].config.isTarget){
37704                         dds[dd].proxyTop.remove();
37705                         dds[dd].proxyBottom.remove();
37706                         dds[dd].unreg();
37707                     }
37708                     if(Roo.dd.DDM.locationCache[dd]){
37709                         delete Roo.dd.DDM.locationCache[dd];
37710                     }
37711                 }
37712                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37713             }
37714         }
37715         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37716         this.bind(null, null);
37717         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37718     },
37719
37720     handleLockChange : function(){
37721         this.refresh(true);
37722     },
37723
37724     onDenyColumnLock : function(){
37725
37726     },
37727
37728     onDenyColumnHide : function(){
37729
37730     },
37731
37732     handleHdMenuClick : function(item){
37733         var index = this.hdCtxIndex;
37734         var cm = this.cm, ds = this.ds;
37735         switch(item.id){
37736             case "asc":
37737                 ds.sort(cm.getDataIndex(index), "ASC");
37738                 break;
37739             case "desc":
37740                 ds.sort(cm.getDataIndex(index), "DESC");
37741                 break;
37742             case "lock":
37743                 var lc = cm.getLockedCount();
37744                 if(cm.getColumnCount(true) <= lc+1){
37745                     this.onDenyColumnLock();
37746                     return;
37747                 }
37748                 if(lc != index){
37749                     cm.setLocked(index, true, true);
37750                     cm.moveColumn(index, lc);
37751                     this.grid.fireEvent("columnmove", index, lc);
37752                 }else{
37753                     cm.setLocked(index, true);
37754                 }
37755             break;
37756             case "unlock":
37757                 var lc = cm.getLockedCount();
37758                 if((lc-1) != index){
37759                     cm.setLocked(index, false, true);
37760                     cm.moveColumn(index, lc-1);
37761                     this.grid.fireEvent("columnmove", index, lc-1);
37762                 }else{
37763                     cm.setLocked(index, false);
37764                 }
37765             break;
37766             default:
37767                 index = cm.getIndexById(item.id.substr(4));
37768                 if(index != -1){
37769                     if(item.checked && cm.getColumnCount(true) <= 1){
37770                         this.onDenyColumnHide();
37771                         return false;
37772                     }
37773                     cm.setHidden(index, item.checked);
37774                 }
37775         }
37776         return true;
37777     },
37778
37779     beforeColMenuShow : function(){
37780         var cm = this.cm,  colCount = cm.getColumnCount();
37781         this.colMenu.removeAll();
37782         for(var i = 0; i < colCount; i++){
37783             this.colMenu.add(new Roo.menu.CheckItem({
37784                 id: "col-"+cm.getColumnId(i),
37785                 text: cm.getColumnHeader(i),
37786                 checked: !cm.isHidden(i),
37787                 hideOnClick:false
37788             }));
37789         }
37790     },
37791
37792     handleHdCtx : function(g, index, e){
37793         e.stopEvent();
37794         var hd = this.getHeaderCell(index);
37795         this.hdCtxIndex = index;
37796         var ms = this.hmenu.items, cm = this.cm;
37797         ms.get("asc").setDisabled(!cm.isSortable(index));
37798         ms.get("desc").setDisabled(!cm.isSortable(index));
37799         if(this.grid.enableColLock !== false){
37800             ms.get("lock").setDisabled(cm.isLocked(index));
37801             ms.get("unlock").setDisabled(!cm.isLocked(index));
37802         }
37803         this.hmenu.show(hd, "tl-bl");
37804     },
37805
37806     handleHdOver : function(e){
37807         var hd = this.findHeaderCell(e.getTarget());
37808         if(hd && !this.headersDisabled){
37809             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37810                this.fly(hd).addClass("x-grid-hd-over");
37811             }
37812         }
37813     },
37814
37815     handleHdOut : function(e){
37816         var hd = this.findHeaderCell(e.getTarget());
37817         if(hd){
37818             this.fly(hd).removeClass("x-grid-hd-over");
37819         }
37820     },
37821
37822     handleSplitDblClick : function(e, t){
37823         var i = this.getCellIndex(t);
37824         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37825             this.autoSizeColumn(i, true);
37826             this.layout();
37827         }
37828     },
37829
37830     render : function(){
37831
37832         var cm = this.cm;
37833         var colCount = cm.getColumnCount();
37834
37835         if(this.grid.monitorWindowResize === true){
37836             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37837         }
37838         var header = this.renderHeaders();
37839         var body = this.templates.body.apply({rows:""});
37840         var html = this.templates.master.apply({
37841             lockedBody: body,
37842             body: body,
37843             lockedHeader: header[0],
37844             header: header[1]
37845         });
37846
37847         //this.updateColumns();
37848
37849         this.grid.getGridEl().dom.innerHTML = html;
37850
37851         this.initElements();
37852         
37853         // a kludge to fix the random scolling effect in webkit
37854         this.el.on("scroll", function() {
37855             this.el.dom.scrollTop=0; // hopefully not recursive..
37856         },this);
37857
37858         this.scroller.on("scroll", this.handleScroll, this);
37859         this.lockedBody.on("mousewheel", this.handleWheel, this);
37860         this.mainBody.on("mousewheel", this.handleWheel, this);
37861
37862         this.mainHd.on("mouseover", this.handleHdOver, this);
37863         this.mainHd.on("mouseout", this.handleHdOut, this);
37864         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37865                 {delegate: "."+this.splitClass});
37866
37867         this.lockedHd.on("mouseover", this.handleHdOver, this);
37868         this.lockedHd.on("mouseout", this.handleHdOut, this);
37869         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37870                 {delegate: "."+this.splitClass});
37871
37872         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37873             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37874         }
37875
37876         this.updateSplitters();
37877
37878         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37879             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37880             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37881         }
37882
37883         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37884             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37885             this.hmenu.add(
37886                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37887                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37888             );
37889             if(this.grid.enableColLock !== false){
37890                 this.hmenu.add('-',
37891                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37892                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37893                 );
37894             }
37895             if(this.grid.enableColumnHide !== false){
37896
37897                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37898                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37899                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37900
37901                 this.hmenu.add('-',
37902                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37903                 );
37904             }
37905             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37906
37907             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37908         }
37909
37910         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37911             this.dd = new Roo.grid.GridDragZone(this.grid, {
37912                 ddGroup : this.grid.ddGroup || 'GridDD'
37913             });
37914             
37915         }
37916
37917         /*
37918         for(var i = 0; i < colCount; i++){
37919             if(cm.isHidden(i)){
37920                 this.hideColumn(i);
37921             }
37922             if(cm.config[i].align){
37923                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37924                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37925             }
37926         }*/
37927         
37928         this.updateHeaderSortState();
37929
37930         this.beforeInitialResize();
37931         this.layout(true);
37932
37933         // two part rendering gives faster view to the user
37934         this.renderPhase2.defer(1, this);
37935     },
37936
37937     renderPhase2 : function(){
37938         // render the rows now
37939         this.refresh();
37940         if(this.grid.autoSizeColumns){
37941             this.autoSizeColumns();
37942         }
37943     },
37944
37945     beforeInitialResize : function(){
37946
37947     },
37948
37949     onColumnSplitterMoved : function(i, w){
37950         this.userResized = true;
37951         var cm = this.grid.colModel;
37952         cm.setColumnWidth(i, w, true);
37953         var cid = cm.getColumnId(i);
37954         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37955         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37956         this.updateSplitters();
37957         this.layout();
37958         this.grid.fireEvent("columnresize", i, w);
37959     },
37960
37961     syncRowHeights : function(startIndex, endIndex){
37962         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37963             startIndex = startIndex || 0;
37964             var mrows = this.getBodyTable().rows;
37965             var lrows = this.getLockedTable().rows;
37966             var len = mrows.length-1;
37967             endIndex = Math.min(endIndex || len, len);
37968             for(var i = startIndex; i <= endIndex; i++){
37969                 var m = mrows[i], l = lrows[i];
37970                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37971                 m.style.height = l.style.height = h + "px";
37972             }
37973         }
37974     },
37975
37976     layout : function(initialRender, is2ndPass){
37977         var g = this.grid;
37978         var auto = g.autoHeight;
37979         var scrollOffset = 16;
37980         var c = g.getGridEl(), cm = this.cm,
37981                 expandCol = g.autoExpandColumn,
37982                 gv = this;
37983         //c.beginMeasure();
37984
37985         if(!c.dom.offsetWidth){ // display:none?
37986             if(initialRender){
37987                 this.lockedWrap.show();
37988                 this.mainWrap.show();
37989             }
37990             return;
37991         }
37992
37993         var hasLock = this.cm.isLocked(0);
37994
37995         var tbh = this.headerPanel.getHeight();
37996         var bbh = this.footerPanel.getHeight();
37997
37998         if(auto){
37999             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38000             var newHeight = ch + c.getBorderWidth("tb");
38001             if(g.maxHeight){
38002                 newHeight = Math.min(g.maxHeight, newHeight);
38003             }
38004             c.setHeight(newHeight);
38005         }
38006
38007         if(g.autoWidth){
38008             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38009         }
38010
38011         var s = this.scroller;
38012
38013         var csize = c.getSize(true);
38014
38015         this.el.setSize(csize.width, csize.height);
38016
38017         this.headerPanel.setWidth(csize.width);
38018         this.footerPanel.setWidth(csize.width);
38019
38020         var hdHeight = this.mainHd.getHeight();
38021         var vw = csize.width;
38022         var vh = csize.height - (tbh + bbh);
38023
38024         s.setSize(vw, vh);
38025
38026         var bt = this.getBodyTable();
38027         var ltWidth = hasLock ?
38028                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38029
38030         var scrollHeight = bt.offsetHeight;
38031         var scrollWidth = ltWidth + bt.offsetWidth;
38032         var vscroll = false, hscroll = false;
38033
38034         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38035
38036         var lw = this.lockedWrap, mw = this.mainWrap;
38037         var lb = this.lockedBody, mb = this.mainBody;
38038
38039         setTimeout(function(){
38040             var t = s.dom.offsetTop;
38041             var w = s.dom.clientWidth,
38042                 h = s.dom.clientHeight;
38043
38044             lw.setTop(t);
38045             lw.setSize(ltWidth, h);
38046
38047             mw.setLeftTop(ltWidth, t);
38048             mw.setSize(w-ltWidth, h);
38049
38050             lb.setHeight(h-hdHeight);
38051             mb.setHeight(h-hdHeight);
38052
38053             if(is2ndPass !== true && !gv.userResized && expandCol){
38054                 // high speed resize without full column calculation
38055                 
38056                 var ci = cm.getIndexById(expandCol);
38057                 if (ci < 0) {
38058                     ci = cm.findColumnIndex(expandCol);
38059                 }
38060                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38061                 var expandId = cm.getColumnId(ci);
38062                 var  tw = cm.getTotalWidth(false);
38063                 var currentWidth = cm.getColumnWidth(ci);
38064                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38065                 if(currentWidth != cw){
38066                     cm.setColumnWidth(ci, cw, true);
38067                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38068                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38069                     gv.updateSplitters();
38070                     gv.layout(false, true);
38071                 }
38072             }
38073
38074             if(initialRender){
38075                 lw.show();
38076                 mw.show();
38077             }
38078             //c.endMeasure();
38079         }, 10);
38080     },
38081
38082     onWindowResize : function(){
38083         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38084             return;
38085         }
38086         this.layout();
38087     },
38088
38089     appendFooter : function(parentEl){
38090         return null;
38091     },
38092
38093     sortAscText : "Sort Ascending",
38094     sortDescText : "Sort Descending",
38095     lockText : "Lock Column",
38096     unlockText : "Unlock Column",
38097     columnsText : "Columns"
38098 });
38099
38100
38101 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38102     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38103     this.proxy.el.addClass('x-grid3-col-dd');
38104 };
38105
38106 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38107     handleMouseDown : function(e){
38108
38109     },
38110
38111     callHandleMouseDown : function(e){
38112         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38113     }
38114 });
38115 /*
38116  * Based on:
38117  * Ext JS Library 1.1.1
38118  * Copyright(c) 2006-2007, Ext JS, LLC.
38119  *
38120  * Originally Released Under LGPL - original licence link has changed is not relivant.
38121  *
38122  * Fork - LGPL
38123  * <script type="text/javascript">
38124  */
38125  
38126 // private
38127 // This is a support class used internally by the Grid components
38128 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38129     this.grid = grid;
38130     this.view = grid.getView();
38131     this.proxy = this.view.resizeProxy;
38132     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38133         "gridSplitters" + this.grid.getGridEl().id, {
38134         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38135     });
38136     this.setHandleElId(Roo.id(hd));
38137     this.setOuterHandleElId(Roo.id(hd2));
38138     this.scroll = false;
38139 };
38140 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38141     fly: Roo.Element.fly,
38142
38143     b4StartDrag : function(x, y){
38144         this.view.headersDisabled = true;
38145         this.proxy.setHeight(this.view.mainWrap.getHeight());
38146         var w = this.cm.getColumnWidth(this.cellIndex);
38147         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38148         this.resetConstraints();
38149         this.setXConstraint(minw, 1000);
38150         this.setYConstraint(0, 0);
38151         this.minX = x - minw;
38152         this.maxX = x + 1000;
38153         this.startPos = x;
38154         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38155     },
38156
38157
38158     handleMouseDown : function(e){
38159         ev = Roo.EventObject.setEvent(e);
38160         var t = this.fly(ev.getTarget());
38161         if(t.hasClass("x-grid-split")){
38162             this.cellIndex = this.view.getCellIndex(t.dom);
38163             this.split = t.dom;
38164             this.cm = this.grid.colModel;
38165             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38166                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38167             }
38168         }
38169     },
38170
38171     endDrag : function(e){
38172         this.view.headersDisabled = false;
38173         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38174         var diff = endX - this.startPos;
38175         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38176     },
38177
38178     autoOffset : function(){
38179         this.setDelta(0,0);
38180     }
38181 });/*
38182  * Based on:
38183  * Ext JS Library 1.1.1
38184  * Copyright(c) 2006-2007, Ext JS, LLC.
38185  *
38186  * Originally Released Under LGPL - original licence link has changed is not relivant.
38187  *
38188  * Fork - LGPL
38189  * <script type="text/javascript">
38190  */
38191  
38192 // private
38193 // This is a support class used internally by the Grid components
38194 Roo.grid.GridDragZone = function(grid, config){
38195     this.view = grid.getView();
38196     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38197     if(this.view.lockedBody){
38198         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38199         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38200     }
38201     this.scroll = false;
38202     this.grid = grid;
38203     this.ddel = document.createElement('div');
38204     this.ddel.className = 'x-grid-dd-wrap';
38205 };
38206
38207 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38208     ddGroup : "GridDD",
38209
38210     getDragData : function(e){
38211         var t = Roo.lib.Event.getTarget(e);
38212         var rowIndex = this.view.findRowIndex(t);
38213         var sm = this.grid.selModel;
38214             
38215         //Roo.log(rowIndex);
38216         
38217         if (sm.getSelectedCell) {
38218             // cell selection..
38219             if (!sm.getSelectedCell()) {
38220                 return false;
38221             }
38222             if (rowIndex != sm.getSelectedCell()[0]) {
38223                 return false;
38224             }
38225         
38226         }
38227         
38228         if(rowIndex !== false){
38229             
38230             // if editorgrid.. 
38231             
38232             
38233             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38234                
38235             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38236               //  
38237             //}
38238             if (e.hasModifier()){
38239                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38240             }
38241             
38242             Roo.log("getDragData");
38243             
38244             return {
38245                 grid: this.grid,
38246                 ddel: this.ddel,
38247                 rowIndex: rowIndex,
38248                 selections:sm.getSelections ? sm.getSelections() : (
38249                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38250                 )
38251             };
38252         }
38253         return false;
38254     },
38255
38256     onInitDrag : function(e){
38257         var data = this.dragData;
38258         this.ddel.innerHTML = this.grid.getDragDropText();
38259         this.proxy.update(this.ddel);
38260         // fire start drag?
38261     },
38262
38263     afterRepair : function(){
38264         this.dragging = false;
38265     },
38266
38267     getRepairXY : function(e, data){
38268         return false;
38269     },
38270
38271     onEndDrag : function(data, e){
38272         // fire end drag?
38273     },
38274
38275     onValidDrop : function(dd, e, id){
38276         // fire drag drop?
38277         this.hideProxy();
38278     },
38279
38280     beforeInvalidDrop : function(e, id){
38281
38282     }
38283 });/*
38284  * Based on:
38285  * Ext JS Library 1.1.1
38286  * Copyright(c) 2006-2007, Ext JS, LLC.
38287  *
38288  * Originally Released Under LGPL - original licence link has changed is not relivant.
38289  *
38290  * Fork - LGPL
38291  * <script type="text/javascript">
38292  */
38293  
38294
38295 /**
38296  * @class Roo.grid.ColumnModel
38297  * @extends Roo.util.Observable
38298  * This is the default implementation of a ColumnModel used by the Grid. It defines
38299  * the columns in the grid.
38300  * <br>Usage:<br>
38301  <pre><code>
38302  var colModel = new Roo.grid.ColumnModel([
38303         {header: "Ticker", width: 60, sortable: true, locked: true},
38304         {header: "Company Name", width: 150, sortable: true},
38305         {header: "Market Cap.", width: 100, sortable: true},
38306         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38307         {header: "Employees", width: 100, sortable: true, resizable: false}
38308  ]);
38309  </code></pre>
38310  * <p>
38311  
38312  * The config options listed for this class are options which may appear in each
38313  * individual column definition.
38314  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38315  * @constructor
38316  * @param {Object} config An Array of column config objects. See this class's
38317  * config objects for details.
38318 */
38319 Roo.grid.ColumnModel = function(config){
38320         /**
38321      * The config passed into the constructor
38322      */
38323     this.config = config;
38324     this.lookup = {};
38325
38326     // if no id, create one
38327     // if the column does not have a dataIndex mapping,
38328     // map it to the order it is in the config
38329     for(var i = 0, len = config.length; i < len; i++){
38330         var c = config[i];
38331         if(typeof c.dataIndex == "undefined"){
38332             c.dataIndex = i;
38333         }
38334         if(typeof c.renderer == "string"){
38335             c.renderer = Roo.util.Format[c.renderer];
38336         }
38337         if(typeof c.id == "undefined"){
38338             c.id = Roo.id();
38339         }
38340         if(c.editor && c.editor.xtype){
38341             c.editor  = Roo.factory(c.editor, Roo.grid);
38342         }
38343         if(c.editor && c.editor.isFormField){
38344             c.editor = new Roo.grid.GridEditor(c.editor);
38345         }
38346         this.lookup[c.id] = c;
38347     }
38348
38349     /**
38350      * The width of columns which have no width specified (defaults to 100)
38351      * @type Number
38352      */
38353     this.defaultWidth = 100;
38354
38355     /**
38356      * Default sortable of columns which have no sortable specified (defaults to false)
38357      * @type Boolean
38358      */
38359     this.defaultSortable = false;
38360
38361     this.addEvents({
38362         /**
38363              * @event widthchange
38364              * Fires when the width of a column changes.
38365              * @param {ColumnModel} this
38366              * @param {Number} columnIndex The column index
38367              * @param {Number} newWidth The new width
38368              */
38369             "widthchange": true,
38370         /**
38371              * @event headerchange
38372              * Fires when the text of a header changes.
38373              * @param {ColumnModel} this
38374              * @param {Number} columnIndex The column index
38375              * @param {Number} newText The new header text
38376              */
38377             "headerchange": true,
38378         /**
38379              * @event hiddenchange
38380              * Fires when a column is hidden or "unhidden".
38381              * @param {ColumnModel} this
38382              * @param {Number} columnIndex The column index
38383              * @param {Boolean} hidden true if hidden, false otherwise
38384              */
38385             "hiddenchange": true,
38386             /**
38387          * @event columnmoved
38388          * Fires when a column is moved.
38389          * @param {ColumnModel} this
38390          * @param {Number} oldIndex
38391          * @param {Number} newIndex
38392          */
38393         "columnmoved" : true,
38394         /**
38395          * @event columlockchange
38396          * Fires when a column's locked state is changed
38397          * @param {ColumnModel} this
38398          * @param {Number} colIndex
38399          * @param {Boolean} locked true if locked
38400          */
38401         "columnlockchange" : true
38402     });
38403     Roo.grid.ColumnModel.superclass.constructor.call(this);
38404 };
38405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38406     /**
38407      * @cfg {String} header The header text to display in the Grid view.
38408      */
38409     /**
38410      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38411      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38412      * specified, the column's index is used as an index into the Record's data Array.
38413      */
38414     /**
38415      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38416      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38417      */
38418     /**
38419      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38420      * Defaults to the value of the {@link #defaultSortable} property.
38421      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38422      */
38423     /**
38424      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38425      */
38426     /**
38427      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38428      */
38429     /**
38430      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38431      */
38432     /**
38433      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38434      */
38435     /**
38436      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38437      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38438      * default renderer uses the raw data value.
38439      */
38440        /**
38441      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38442      */
38443     /**
38444      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38445      */
38446
38447     /**
38448      * Returns the id of the column at the specified index.
38449      * @param {Number} index The column index
38450      * @return {String} the id
38451      */
38452     getColumnId : function(index){
38453         return this.config[index].id;
38454     },
38455
38456     /**
38457      * Returns the column for a specified id.
38458      * @param {String} id The column id
38459      * @return {Object} the column
38460      */
38461     getColumnById : function(id){
38462         return this.lookup[id];
38463     },
38464
38465     
38466     /**
38467      * Returns the column for a specified dataIndex.
38468      * @param {String} dataIndex The column dataIndex
38469      * @return {Object|Boolean} the column or false if not found
38470      */
38471     getColumnByDataIndex: function(dataIndex){
38472         var index = this.findColumnIndex(dataIndex);
38473         return index > -1 ? this.config[index] : false;
38474     },
38475     
38476     /**
38477      * Returns the index for a specified column id.
38478      * @param {String} id The column id
38479      * @return {Number} the index, or -1 if not found
38480      */
38481     getIndexById : function(id){
38482         for(var i = 0, len = this.config.length; i < len; i++){
38483             if(this.config[i].id == id){
38484                 return i;
38485             }
38486         }
38487         return -1;
38488     },
38489     
38490     /**
38491      * Returns the index for a specified column dataIndex.
38492      * @param {String} dataIndex The column dataIndex
38493      * @return {Number} the index, or -1 if not found
38494      */
38495     
38496     findColumnIndex : function(dataIndex){
38497         for(var i = 0, len = this.config.length; i < len; i++){
38498             if(this.config[i].dataIndex == dataIndex){
38499                 return i;
38500             }
38501         }
38502         return -1;
38503     },
38504     
38505     
38506     moveColumn : function(oldIndex, newIndex){
38507         var c = this.config[oldIndex];
38508         this.config.splice(oldIndex, 1);
38509         this.config.splice(newIndex, 0, c);
38510         this.dataMap = null;
38511         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38512     },
38513
38514     isLocked : function(colIndex){
38515         return this.config[colIndex].locked === true;
38516     },
38517
38518     setLocked : function(colIndex, value, suppressEvent){
38519         if(this.isLocked(colIndex) == value){
38520             return;
38521         }
38522         this.config[colIndex].locked = value;
38523         if(!suppressEvent){
38524             this.fireEvent("columnlockchange", this, colIndex, value);
38525         }
38526     },
38527
38528     getTotalLockedWidth : function(){
38529         var totalWidth = 0;
38530         for(var i = 0; i < this.config.length; i++){
38531             if(this.isLocked(i) && !this.isHidden(i)){
38532                 this.totalWidth += this.getColumnWidth(i);
38533             }
38534         }
38535         return totalWidth;
38536     },
38537
38538     getLockedCount : function(){
38539         for(var i = 0, len = this.config.length; i < len; i++){
38540             if(!this.isLocked(i)){
38541                 return i;
38542             }
38543         }
38544     },
38545
38546     /**
38547      * Returns the number of columns.
38548      * @return {Number}
38549      */
38550     getColumnCount : function(visibleOnly){
38551         if(visibleOnly === true){
38552             var c = 0;
38553             for(var i = 0, len = this.config.length; i < len; i++){
38554                 if(!this.isHidden(i)){
38555                     c++;
38556                 }
38557             }
38558             return c;
38559         }
38560         return this.config.length;
38561     },
38562
38563     /**
38564      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38565      * @param {Function} fn
38566      * @param {Object} scope (optional)
38567      * @return {Array} result
38568      */
38569     getColumnsBy : function(fn, scope){
38570         var r = [];
38571         for(var i = 0, len = this.config.length; i < len; i++){
38572             var c = this.config[i];
38573             if(fn.call(scope||this, c, i) === true){
38574                 r[r.length] = c;
38575             }
38576         }
38577         return r;
38578     },
38579
38580     /**
38581      * Returns true if the specified column is sortable.
38582      * @param {Number} col The column index
38583      * @return {Boolean}
38584      */
38585     isSortable : function(col){
38586         if(typeof this.config[col].sortable == "undefined"){
38587             return this.defaultSortable;
38588         }
38589         return this.config[col].sortable;
38590     },
38591
38592     /**
38593      * Returns the rendering (formatting) function defined for the column.
38594      * @param {Number} col The column index.
38595      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38596      */
38597     getRenderer : function(col){
38598         if(!this.config[col].renderer){
38599             return Roo.grid.ColumnModel.defaultRenderer;
38600         }
38601         return this.config[col].renderer;
38602     },
38603
38604     /**
38605      * Sets the rendering (formatting) function for a column.
38606      * @param {Number} col The column index
38607      * @param {Function} fn The function to use to process the cell's raw data
38608      * to return HTML markup for the grid view. The render function is called with
38609      * the following parameters:<ul>
38610      * <li>Data value.</li>
38611      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38612      * <li>css A CSS style string to apply to the table cell.</li>
38613      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38614      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38615      * <li>Row index</li>
38616      * <li>Column index</li>
38617      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38618      */
38619     setRenderer : function(col, fn){
38620         this.config[col].renderer = fn;
38621     },
38622
38623     /**
38624      * Returns the width for the specified column.
38625      * @param {Number} col The column index
38626      * @return {Number}
38627      */
38628     getColumnWidth : function(col){
38629         return this.config[col].width * 1 || this.defaultWidth;
38630     },
38631
38632     /**
38633      * Sets the width for a column.
38634      * @param {Number} col The column index
38635      * @param {Number} width The new width
38636      */
38637     setColumnWidth : function(col, width, suppressEvent){
38638         this.config[col].width = width;
38639         this.totalWidth = null;
38640         if(!suppressEvent){
38641              this.fireEvent("widthchange", this, col, width);
38642         }
38643     },
38644
38645     /**
38646      * Returns the total width of all columns.
38647      * @param {Boolean} includeHidden True to include hidden column widths
38648      * @return {Number}
38649      */
38650     getTotalWidth : function(includeHidden){
38651         if(!this.totalWidth){
38652             this.totalWidth = 0;
38653             for(var i = 0, len = this.config.length; i < len; i++){
38654                 if(includeHidden || !this.isHidden(i)){
38655                     this.totalWidth += this.getColumnWidth(i);
38656                 }
38657             }
38658         }
38659         return this.totalWidth;
38660     },
38661
38662     /**
38663      * Returns the header for the specified column.
38664      * @param {Number} col The column index
38665      * @return {String}
38666      */
38667     getColumnHeader : function(col){
38668         return this.config[col].header;
38669     },
38670
38671     /**
38672      * Sets the header for a column.
38673      * @param {Number} col The column index
38674      * @param {String} header The new header
38675      */
38676     setColumnHeader : function(col, header){
38677         this.config[col].header = header;
38678         this.fireEvent("headerchange", this, col, header);
38679     },
38680
38681     /**
38682      * Returns the tooltip for the specified column.
38683      * @param {Number} col The column index
38684      * @return {String}
38685      */
38686     getColumnTooltip : function(col){
38687             return this.config[col].tooltip;
38688     },
38689     /**
38690      * Sets the tooltip for a column.
38691      * @param {Number} col The column index
38692      * @param {String} tooltip The new tooltip
38693      */
38694     setColumnTooltip : function(col, tooltip){
38695             this.config[col].tooltip = tooltip;
38696     },
38697
38698     /**
38699      * Returns the dataIndex for the specified column.
38700      * @param {Number} col The column index
38701      * @return {Number}
38702      */
38703     getDataIndex : function(col){
38704         return this.config[col].dataIndex;
38705     },
38706
38707     /**
38708      * Sets the dataIndex for a column.
38709      * @param {Number} col The column index
38710      * @param {Number} dataIndex The new dataIndex
38711      */
38712     setDataIndex : function(col, dataIndex){
38713         this.config[col].dataIndex = dataIndex;
38714     },
38715
38716     
38717     
38718     /**
38719      * Returns true if the cell is editable.
38720      * @param {Number} colIndex The column index
38721      * @param {Number} rowIndex The row index
38722      * @return {Boolean}
38723      */
38724     isCellEditable : function(colIndex, rowIndex){
38725         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38726     },
38727
38728     /**
38729      * Returns the editor defined for the cell/column.
38730      * return false or null to disable editing.
38731      * @param {Number} colIndex The column index
38732      * @param {Number} rowIndex The row index
38733      * @return {Object}
38734      */
38735     getCellEditor : function(colIndex, rowIndex){
38736         return this.config[colIndex].editor;
38737     },
38738
38739     /**
38740      * Sets if a column is editable.
38741      * @param {Number} col The column index
38742      * @param {Boolean} editable True if the column is editable
38743      */
38744     setEditable : function(col, editable){
38745         this.config[col].editable = editable;
38746     },
38747
38748
38749     /**
38750      * Returns true if the column is hidden.
38751      * @param {Number} colIndex The column index
38752      * @return {Boolean}
38753      */
38754     isHidden : function(colIndex){
38755         return this.config[colIndex].hidden;
38756     },
38757
38758
38759     /**
38760      * Returns true if the column width cannot be changed
38761      */
38762     isFixed : function(colIndex){
38763         return this.config[colIndex].fixed;
38764     },
38765
38766     /**
38767      * Returns true if the column can be resized
38768      * @return {Boolean}
38769      */
38770     isResizable : function(colIndex){
38771         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38772     },
38773     /**
38774      * Sets if a column is hidden.
38775      * @param {Number} colIndex The column index
38776      * @param {Boolean} hidden True if the column is hidden
38777      */
38778     setHidden : function(colIndex, hidden){
38779         this.config[colIndex].hidden = hidden;
38780         this.totalWidth = null;
38781         this.fireEvent("hiddenchange", this, colIndex, hidden);
38782     },
38783
38784     /**
38785      * Sets the editor for a column.
38786      * @param {Number} col The column index
38787      * @param {Object} editor The editor object
38788      */
38789     setEditor : function(col, editor){
38790         this.config[col].editor = editor;
38791     }
38792 });
38793
38794 Roo.grid.ColumnModel.defaultRenderer = function(value){
38795         if(typeof value == "string" && value.length < 1){
38796             return "&#160;";
38797         }
38798         return value;
38799 };
38800
38801 // Alias for backwards compatibility
38802 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38803 /*
38804  * Based on:
38805  * Ext JS Library 1.1.1
38806  * Copyright(c) 2006-2007, Ext JS, LLC.
38807  *
38808  * Originally Released Under LGPL - original licence link has changed is not relivant.
38809  *
38810  * Fork - LGPL
38811  * <script type="text/javascript">
38812  */
38813
38814 /**
38815  * @class Roo.grid.AbstractSelectionModel
38816  * @extends Roo.util.Observable
38817  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38818  * implemented by descendant classes.  This class should not be directly instantiated.
38819  * @constructor
38820  */
38821 Roo.grid.AbstractSelectionModel = function(){
38822     this.locked = false;
38823     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38824 };
38825
38826 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38827     /** @ignore Called by the grid automatically. Do not call directly. */
38828     init : function(grid){
38829         this.grid = grid;
38830         this.initEvents();
38831     },
38832
38833     /**
38834      * Locks the selections.
38835      */
38836     lock : function(){
38837         this.locked = true;
38838     },
38839
38840     /**
38841      * Unlocks the selections.
38842      */
38843     unlock : function(){
38844         this.locked = false;
38845     },
38846
38847     /**
38848      * Returns true if the selections are locked.
38849      * @return {Boolean}
38850      */
38851     isLocked : function(){
38852         return this.locked;
38853     }
38854 });/*
38855  * Based on:
38856  * Ext JS Library 1.1.1
38857  * Copyright(c) 2006-2007, Ext JS, LLC.
38858  *
38859  * Originally Released Under LGPL - original licence link has changed is not relivant.
38860  *
38861  * Fork - LGPL
38862  * <script type="text/javascript">
38863  */
38864 /**
38865  * @extends Roo.grid.AbstractSelectionModel
38866  * @class Roo.grid.RowSelectionModel
38867  * The default SelectionModel used by {@link Roo.grid.Grid}.
38868  * It supports multiple selections and keyboard selection/navigation. 
38869  * @constructor
38870  * @param {Object} config
38871  */
38872 Roo.grid.RowSelectionModel = function(config){
38873     Roo.apply(this, config);
38874     this.selections = new Roo.util.MixedCollection(false, function(o){
38875         return o.id;
38876     });
38877
38878     this.last = false;
38879     this.lastActive = false;
38880
38881     this.addEvents({
38882         /**
38883              * @event selectionchange
38884              * Fires when the selection changes
38885              * @param {SelectionModel} this
38886              */
38887             "selectionchange" : true,
38888         /**
38889              * @event afterselectionchange
38890              * Fires after the selection changes (eg. by key press or clicking)
38891              * @param {SelectionModel} this
38892              */
38893             "afterselectionchange" : true,
38894         /**
38895              * @event beforerowselect
38896              * Fires when a row is selected being selected, return false to cancel.
38897              * @param {SelectionModel} this
38898              * @param {Number} rowIndex The selected index
38899              * @param {Boolean} keepExisting False if other selections will be cleared
38900              */
38901             "beforerowselect" : true,
38902         /**
38903              * @event rowselect
38904              * Fires when a row is selected.
38905              * @param {SelectionModel} this
38906              * @param {Number} rowIndex The selected index
38907              * @param {Roo.data.Record} r The record
38908              */
38909             "rowselect" : true,
38910         /**
38911              * @event rowdeselect
38912              * Fires when a row is deselected.
38913              * @param {SelectionModel} this
38914              * @param {Number} rowIndex The selected index
38915              */
38916         "rowdeselect" : true
38917     });
38918     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38919     this.locked = false;
38920 };
38921
38922 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38923     /**
38924      * @cfg {Boolean} singleSelect
38925      * True to allow selection of only one row at a time (defaults to false)
38926      */
38927     singleSelect : false,
38928
38929     // private
38930     initEvents : function(){
38931
38932         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38933             this.grid.on("mousedown", this.handleMouseDown, this);
38934         }else{ // allow click to work like normal
38935             this.grid.on("rowclick", this.handleDragableRowClick, this);
38936         }
38937
38938         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38939             "up" : function(e){
38940                 if(!e.shiftKey){
38941                     this.selectPrevious(e.shiftKey);
38942                 }else if(this.last !== false && this.lastActive !== false){
38943                     var last = this.last;
38944                     this.selectRange(this.last,  this.lastActive-1);
38945                     this.grid.getView().focusRow(this.lastActive);
38946                     if(last !== false){
38947                         this.last = last;
38948                     }
38949                 }else{
38950                     this.selectFirstRow();
38951                 }
38952                 this.fireEvent("afterselectionchange", this);
38953             },
38954             "down" : function(e){
38955                 if(!e.shiftKey){
38956                     this.selectNext(e.shiftKey);
38957                 }else if(this.last !== false && this.lastActive !== false){
38958                     var last = this.last;
38959                     this.selectRange(this.last,  this.lastActive+1);
38960                     this.grid.getView().focusRow(this.lastActive);
38961                     if(last !== false){
38962                         this.last = last;
38963                     }
38964                 }else{
38965                     this.selectFirstRow();
38966                 }
38967                 this.fireEvent("afterselectionchange", this);
38968             },
38969             scope: this
38970         });
38971
38972         var view = this.grid.view;
38973         view.on("refresh", this.onRefresh, this);
38974         view.on("rowupdated", this.onRowUpdated, this);
38975         view.on("rowremoved", this.onRemove, this);
38976     },
38977
38978     // private
38979     onRefresh : function(){
38980         var ds = this.grid.dataSource, i, v = this.grid.view;
38981         var s = this.selections;
38982         s.each(function(r){
38983             if((i = ds.indexOfId(r.id)) != -1){
38984                 v.onRowSelect(i);
38985             }else{
38986                 s.remove(r);
38987             }
38988         });
38989     },
38990
38991     // private
38992     onRemove : function(v, index, r){
38993         this.selections.remove(r);
38994     },
38995
38996     // private
38997     onRowUpdated : function(v, index, r){
38998         if(this.isSelected(r)){
38999             v.onRowSelect(index);
39000         }
39001     },
39002
39003     /**
39004      * Select records.
39005      * @param {Array} records The records to select
39006      * @param {Boolean} keepExisting (optional) True to keep existing selections
39007      */
39008     selectRecords : function(records, keepExisting){
39009         if(!keepExisting){
39010             this.clearSelections();
39011         }
39012         var ds = this.grid.dataSource;
39013         for(var i = 0, len = records.length; i < len; i++){
39014             this.selectRow(ds.indexOf(records[i]), true);
39015         }
39016     },
39017
39018     /**
39019      * Gets the number of selected rows.
39020      * @return {Number}
39021      */
39022     getCount : function(){
39023         return this.selections.length;
39024     },
39025
39026     /**
39027      * Selects the first row in the grid.
39028      */
39029     selectFirstRow : function(){
39030         this.selectRow(0);
39031     },
39032
39033     /**
39034      * Select the last row.
39035      * @param {Boolean} keepExisting (optional) True to keep existing selections
39036      */
39037     selectLastRow : function(keepExisting){
39038         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39039     },
39040
39041     /**
39042      * Selects the row immediately following the last selected row.
39043      * @param {Boolean} keepExisting (optional) True to keep existing selections
39044      */
39045     selectNext : function(keepExisting){
39046         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39047             this.selectRow(this.last+1, keepExisting);
39048             this.grid.getView().focusRow(this.last);
39049         }
39050     },
39051
39052     /**
39053      * Selects the row that precedes the last selected row.
39054      * @param {Boolean} keepExisting (optional) True to keep existing selections
39055      */
39056     selectPrevious : function(keepExisting){
39057         if(this.last){
39058             this.selectRow(this.last-1, keepExisting);
39059             this.grid.getView().focusRow(this.last);
39060         }
39061     },
39062
39063     /**
39064      * Returns the selected records
39065      * @return {Array} Array of selected records
39066      */
39067     getSelections : function(){
39068         return [].concat(this.selections.items);
39069     },
39070
39071     /**
39072      * Returns the first selected record.
39073      * @return {Record}
39074      */
39075     getSelected : function(){
39076         return this.selections.itemAt(0);
39077     },
39078
39079
39080     /**
39081      * Clears all selections.
39082      */
39083     clearSelections : function(fast){
39084         if(this.locked) return;
39085         if(fast !== true){
39086             var ds = this.grid.dataSource;
39087             var s = this.selections;
39088             s.each(function(r){
39089                 this.deselectRow(ds.indexOfId(r.id));
39090             }, this);
39091             s.clear();
39092         }else{
39093             this.selections.clear();
39094         }
39095         this.last = false;
39096     },
39097
39098
39099     /**
39100      * Selects all rows.
39101      */
39102     selectAll : function(){
39103         if(this.locked) return;
39104         this.selections.clear();
39105         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39106             this.selectRow(i, true);
39107         }
39108     },
39109
39110     /**
39111      * Returns True if there is a selection.
39112      * @return {Boolean}
39113      */
39114     hasSelection : function(){
39115         return this.selections.length > 0;
39116     },
39117
39118     /**
39119      * Returns True if the specified row is selected.
39120      * @param {Number/Record} record The record or index of the record to check
39121      * @return {Boolean}
39122      */
39123     isSelected : function(index){
39124         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39125         return (r && this.selections.key(r.id) ? true : false);
39126     },
39127
39128     /**
39129      * Returns True if the specified record id is selected.
39130      * @param {String} id The id of record to check
39131      * @return {Boolean}
39132      */
39133     isIdSelected : function(id){
39134         return (this.selections.key(id) ? true : false);
39135     },
39136
39137     // private
39138     handleMouseDown : function(e, t){
39139         var view = this.grid.getView(), rowIndex;
39140         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39141             return;
39142         };
39143         if(e.shiftKey && this.last !== false){
39144             var last = this.last;
39145             this.selectRange(last, rowIndex, e.ctrlKey);
39146             this.last = last; // reset the last
39147             view.focusRow(rowIndex);
39148         }else{
39149             var isSelected = this.isSelected(rowIndex);
39150             if(e.button !== 0 && isSelected){
39151                 view.focusRow(rowIndex);
39152             }else if(e.ctrlKey && isSelected){
39153                 this.deselectRow(rowIndex);
39154             }else if(!isSelected){
39155                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39156                 view.focusRow(rowIndex);
39157             }
39158         }
39159         this.fireEvent("afterselectionchange", this);
39160     },
39161     // private
39162     handleDragableRowClick :  function(grid, rowIndex, e) 
39163     {
39164         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39165             this.selectRow(rowIndex, false);
39166             grid.view.focusRow(rowIndex);
39167              this.fireEvent("afterselectionchange", this);
39168         }
39169     },
39170     
39171     /**
39172      * Selects multiple rows.
39173      * @param {Array} rows Array of the indexes of the row to select
39174      * @param {Boolean} keepExisting (optional) True to keep existing selections
39175      */
39176     selectRows : function(rows, keepExisting){
39177         if(!keepExisting){
39178             this.clearSelections();
39179         }
39180         for(var i = 0, len = rows.length; i < len; i++){
39181             this.selectRow(rows[i], true);
39182         }
39183     },
39184
39185     /**
39186      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39187      * @param {Number} startRow The index of the first row in the range
39188      * @param {Number} endRow The index of the last row in the range
39189      * @param {Boolean} keepExisting (optional) True to retain existing selections
39190      */
39191     selectRange : function(startRow, endRow, keepExisting){
39192         if(this.locked) return;
39193         if(!keepExisting){
39194             this.clearSelections();
39195         }
39196         if(startRow <= endRow){
39197             for(var i = startRow; i <= endRow; i++){
39198                 this.selectRow(i, true);
39199             }
39200         }else{
39201             for(var i = startRow; i >= endRow; i--){
39202                 this.selectRow(i, true);
39203             }
39204         }
39205     },
39206
39207     /**
39208      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39209      * @param {Number} startRow The index of the first row in the range
39210      * @param {Number} endRow The index of the last row in the range
39211      */
39212     deselectRange : function(startRow, endRow, preventViewNotify){
39213         if(this.locked) return;
39214         for(var i = startRow; i <= endRow; i++){
39215             this.deselectRow(i, preventViewNotify);
39216         }
39217     },
39218
39219     /**
39220      * Selects a row.
39221      * @param {Number} row The index of the row to select
39222      * @param {Boolean} keepExisting (optional) True to keep existing selections
39223      */
39224     selectRow : function(index, keepExisting, preventViewNotify){
39225         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39226         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39227             if(!keepExisting || this.singleSelect){
39228                 this.clearSelections();
39229             }
39230             var r = this.grid.dataSource.getAt(index);
39231             this.selections.add(r);
39232             this.last = this.lastActive = index;
39233             if(!preventViewNotify){
39234                 this.grid.getView().onRowSelect(index);
39235             }
39236             this.fireEvent("rowselect", this, index, r);
39237             this.fireEvent("selectionchange", this);
39238         }
39239     },
39240
39241     /**
39242      * Deselects a row.
39243      * @param {Number} row The index of the row to deselect
39244      */
39245     deselectRow : function(index, preventViewNotify){
39246         if(this.locked) return;
39247         if(this.last == index){
39248             this.last = false;
39249         }
39250         if(this.lastActive == index){
39251             this.lastActive = false;
39252         }
39253         var r = this.grid.dataSource.getAt(index);
39254         this.selections.remove(r);
39255         if(!preventViewNotify){
39256             this.grid.getView().onRowDeselect(index);
39257         }
39258         this.fireEvent("rowdeselect", this, index);
39259         this.fireEvent("selectionchange", this);
39260     },
39261
39262     // private
39263     restoreLast : function(){
39264         if(this._last){
39265             this.last = this._last;
39266         }
39267     },
39268
39269     // private
39270     acceptsNav : function(row, col, cm){
39271         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39272     },
39273
39274     // private
39275     onEditorKey : function(field, e){
39276         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39277         if(k == e.TAB){
39278             e.stopEvent();
39279             ed.completeEdit();
39280             if(e.shiftKey){
39281                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39282             }else{
39283                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39284             }
39285         }else if(k == e.ENTER && !e.ctrlKey){
39286             e.stopEvent();
39287             ed.completeEdit();
39288             if(e.shiftKey){
39289                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39290             }else{
39291                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39292             }
39293         }else if(k == e.ESC){
39294             ed.cancelEdit();
39295         }
39296         if(newCell){
39297             g.startEditing(newCell[0], newCell[1]);
39298         }
39299     }
39300 });/*
39301  * Based on:
39302  * Ext JS Library 1.1.1
39303  * Copyright(c) 2006-2007, Ext JS, LLC.
39304  *
39305  * Originally Released Under LGPL - original licence link has changed is not relivant.
39306  *
39307  * Fork - LGPL
39308  * <script type="text/javascript">
39309  */
39310 /**
39311  * @class Roo.grid.CellSelectionModel
39312  * @extends Roo.grid.AbstractSelectionModel
39313  * This class provides the basic implementation for cell selection in a grid.
39314  * @constructor
39315  * @param {Object} config The object containing the configuration of this model.
39316  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39317  */
39318 Roo.grid.CellSelectionModel = function(config){
39319     Roo.apply(this, config);
39320
39321     this.selection = null;
39322
39323     this.addEvents({
39324         /**
39325              * @event beforerowselect
39326              * Fires before a cell is selected.
39327              * @param {SelectionModel} this
39328              * @param {Number} rowIndex The selected row index
39329              * @param {Number} colIndex The selected cell index
39330              */
39331             "beforecellselect" : true,
39332         /**
39333              * @event cellselect
39334              * Fires when a cell is selected.
39335              * @param {SelectionModel} this
39336              * @param {Number} rowIndex The selected row index
39337              * @param {Number} colIndex The selected cell index
39338              */
39339             "cellselect" : true,
39340         /**
39341              * @event selectionchange
39342              * Fires when the active selection changes.
39343              * @param {SelectionModel} this
39344              * @param {Object} selection null for no selection or an object (o) with two properties
39345                 <ul>
39346                 <li>o.record: the record object for the row the selection is in</li>
39347                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39348                 </ul>
39349              */
39350             "selectionchange" : true,
39351         /**
39352              * @event tabend
39353              * Fires when the tab (or enter) was pressed on the last editable cell
39354              * You can use this to trigger add new row.
39355              * @param {SelectionModel} this
39356              */
39357             "tabend" : true,
39358          /**
39359              * @event beforeeditnext
39360              * Fires before the next editable sell is made active
39361              * You can use this to skip to another cell or fire the tabend
39362              *    if you set cell to false
39363              * @param {Object} eventdata object : { cell : [ row, col ] } 
39364              */
39365             "beforeeditnext" : true
39366     });
39367     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39368 };
39369
39370 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39371     
39372     enter_is_tab: false,
39373
39374     /** @ignore */
39375     initEvents : function(){
39376         this.grid.on("mousedown", this.handleMouseDown, this);
39377         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39378         var view = this.grid.view;
39379         view.on("refresh", this.onViewChange, this);
39380         view.on("rowupdated", this.onRowUpdated, this);
39381         view.on("beforerowremoved", this.clearSelections, this);
39382         view.on("beforerowsinserted", this.clearSelections, this);
39383         if(this.grid.isEditor){
39384             this.grid.on("beforeedit", this.beforeEdit,  this);
39385         }
39386     },
39387
39388         //private
39389     beforeEdit : function(e){
39390         this.select(e.row, e.column, false, true, e.record);
39391     },
39392
39393         //private
39394     onRowUpdated : function(v, index, r){
39395         if(this.selection && this.selection.record == r){
39396             v.onCellSelect(index, this.selection.cell[1]);
39397         }
39398     },
39399
39400         //private
39401     onViewChange : function(){
39402         this.clearSelections(true);
39403     },
39404
39405         /**
39406          * Returns the currently selected cell,.
39407          * @return {Array} The selected cell (row, column) or null if none selected.
39408          */
39409     getSelectedCell : function(){
39410         return this.selection ? this.selection.cell : null;
39411     },
39412
39413     /**
39414      * Clears all selections.
39415      * @param {Boolean} true to prevent the gridview from being notified about the change.
39416      */
39417     clearSelections : function(preventNotify){
39418         var s = this.selection;
39419         if(s){
39420             if(preventNotify !== true){
39421                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39422             }
39423             this.selection = null;
39424             this.fireEvent("selectionchange", this, null);
39425         }
39426     },
39427
39428     /**
39429      * Returns true if there is a selection.
39430      * @return {Boolean}
39431      */
39432     hasSelection : function(){
39433         return this.selection ? true : false;
39434     },
39435
39436     /** @ignore */
39437     handleMouseDown : function(e, t){
39438         var v = this.grid.getView();
39439         if(this.isLocked()){
39440             return;
39441         };
39442         var row = v.findRowIndex(t);
39443         var cell = v.findCellIndex(t);
39444         if(row !== false && cell !== false){
39445             this.select(row, cell);
39446         }
39447     },
39448
39449     /**
39450      * Selects a cell.
39451      * @param {Number} rowIndex
39452      * @param {Number} collIndex
39453      */
39454     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39455         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39456             this.clearSelections();
39457             r = r || this.grid.dataSource.getAt(rowIndex);
39458             this.selection = {
39459                 record : r,
39460                 cell : [rowIndex, colIndex]
39461             };
39462             if(!preventViewNotify){
39463                 var v = this.grid.getView();
39464                 v.onCellSelect(rowIndex, colIndex);
39465                 if(preventFocus !== true){
39466                     v.focusCell(rowIndex, colIndex);
39467                 }
39468             }
39469             this.fireEvent("cellselect", this, rowIndex, colIndex);
39470             this.fireEvent("selectionchange", this, this.selection);
39471         }
39472     },
39473
39474         //private
39475     isSelectable : function(rowIndex, colIndex, cm){
39476         return !cm.isHidden(colIndex);
39477     },
39478
39479     /** @ignore */
39480     handleKeyDown : function(e){
39481         //Roo.log('Cell Sel Model handleKeyDown');
39482         if(!e.isNavKeyPress()){
39483             return;
39484         }
39485         var g = this.grid, s = this.selection;
39486         if(!s){
39487             e.stopEvent();
39488             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39489             if(cell){
39490                 this.select(cell[0], cell[1]);
39491             }
39492             return;
39493         }
39494         var sm = this;
39495         var walk = function(row, col, step){
39496             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39497         };
39498         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39499         var newCell;
39500
39501       
39502
39503         switch(k){
39504             case e.TAB:
39505                 // handled by onEditorKey
39506                 if (g.isEditor && g.editing) {
39507                     return;
39508                 }
39509                 if(e.shiftKey) {
39510                     newCell = walk(r, c-1, -1);
39511                 } else {
39512                     newCell = walk(r, c+1, 1);
39513                 }
39514                 break;
39515             
39516             case e.DOWN:
39517                newCell = walk(r+1, c, 1);
39518                 break;
39519             
39520             case e.UP:
39521                 newCell = walk(r-1, c, -1);
39522                 break;
39523             
39524             case e.RIGHT:
39525                 newCell = walk(r, c+1, 1);
39526                 break;
39527             
39528             case e.LEFT:
39529                 newCell = walk(r, c-1, -1);
39530                 break;
39531             
39532             case e.ENTER:
39533                 
39534                 if(g.isEditor && !g.editing){
39535                    g.startEditing(r, c);
39536                    e.stopEvent();
39537                    return;
39538                 }
39539                 
39540                 
39541              break;
39542         };
39543         if(newCell){
39544             this.select(newCell[0], newCell[1]);
39545             e.stopEvent();
39546             
39547         }
39548     },
39549
39550     acceptsNav : function(row, col, cm){
39551         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39552     },
39553     /**
39554      * Selects a cell.
39555      * @param {Number} field (not used) - as it's normally used as a listener
39556      * @param {Number} e - event - fake it by using
39557      *
39558      * var e = Roo.EventObjectImpl.prototype;
39559      * e.keyCode = e.TAB
39560      *
39561      * 
39562      */
39563     onEditorKey : function(field, e){
39564         
39565         var k = e.getKey(),
39566             newCell,
39567             g = this.grid,
39568             ed = g.activeEditor,
39569             forward = false;
39570         ///Roo.log('onEditorKey' + k);
39571         
39572         
39573         if (this.enter_is_tab && k == e.ENTER) {
39574             k = e.TAB;
39575         }
39576         
39577         if(k == e.TAB){
39578             if(e.shiftKey){
39579                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39580             }else{
39581                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39582                 forward = true;
39583             }
39584             
39585             e.stopEvent();
39586             
39587         } else if(k == e.ENTER &&  !e.ctrlKey){
39588             ed.completeEdit();
39589             e.stopEvent();
39590             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39591         
39592                 } else if(k == e.ESC){
39593             ed.cancelEdit();
39594         }
39595                 
39596         if (newCell) {
39597             var ecall = { cell : newCell, forward : forward };
39598             this.fireEvent('beforeeditnext', ecall );
39599             newCell = ecall.cell;
39600                         forward = ecall.forward;
39601         }
39602                 
39603         if(newCell){
39604             //Roo.log('next cell after edit');
39605             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39606         } else if (forward) {
39607             // tabbed past last
39608             this.fireEvent.defer(100, this, ['tabend',this]);
39609         }
39610     }
39611 });/*
39612  * Based on:
39613  * Ext JS Library 1.1.1
39614  * Copyright(c) 2006-2007, Ext JS, LLC.
39615  *
39616  * Originally Released Under LGPL - original licence link has changed is not relivant.
39617  *
39618  * Fork - LGPL
39619  * <script type="text/javascript">
39620  */
39621  
39622 /**
39623  * @class Roo.grid.EditorGrid
39624  * @extends Roo.grid.Grid
39625  * Class for creating and editable grid.
39626  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39627  * The container MUST have some type of size defined for the grid to fill. The container will be 
39628  * automatically set to position relative if it isn't already.
39629  * @param {Object} dataSource The data model to bind to
39630  * @param {Object} colModel The column model with info about this grid's columns
39631  */
39632 Roo.grid.EditorGrid = function(container, config){
39633     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39634     this.getGridEl().addClass("xedit-grid");
39635
39636     if(!this.selModel){
39637         this.selModel = new Roo.grid.CellSelectionModel();
39638     }
39639
39640     this.activeEditor = null;
39641
39642         this.addEvents({
39643             /**
39644              * @event beforeedit
39645              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39646              * <ul style="padding:5px;padding-left:16px;">
39647              * <li>grid - This grid</li>
39648              * <li>record - The record being edited</li>
39649              * <li>field - The field name being edited</li>
39650              * <li>value - The value for the field being edited.</li>
39651              * <li>row - The grid row index</li>
39652              * <li>column - The grid column index</li>
39653              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39654              * </ul>
39655              * @param {Object} e An edit event (see above for description)
39656              */
39657             "beforeedit" : true,
39658             /**
39659              * @event afteredit
39660              * Fires after a cell is edited. <br />
39661              * <ul style="padding:5px;padding-left:16px;">
39662              * <li>grid - This grid</li>
39663              * <li>record - The record being edited</li>
39664              * <li>field - The field name being edited</li>
39665              * <li>value - The value being set</li>
39666              * <li>originalValue - The original value for the field, before the edit.</li>
39667              * <li>row - The grid row index</li>
39668              * <li>column - The grid column index</li>
39669              * </ul>
39670              * @param {Object} e An edit event (see above for description)
39671              */
39672             "afteredit" : true,
39673             /**
39674              * @event validateedit
39675              * Fires after a cell is edited, but before the value is set in the record. 
39676          * You can use this to modify the value being set in the field, Return false
39677              * to cancel the change. The edit event object has the following properties <br />
39678              * <ul style="padding:5px;padding-left:16px;">
39679          * <li>editor - This editor</li>
39680              * <li>grid - This grid</li>
39681              * <li>record - The record being edited</li>
39682              * <li>field - The field name being edited</li>
39683              * <li>value - The value being set</li>
39684              * <li>originalValue - The original value for the field, before the edit.</li>
39685              * <li>row - The grid row index</li>
39686              * <li>column - The grid column index</li>
39687              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39688              * </ul>
39689              * @param {Object} e An edit event (see above for description)
39690              */
39691             "validateedit" : true
39692         });
39693     this.on("bodyscroll", this.stopEditing,  this);
39694     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39695 };
39696
39697 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39698     /**
39699      * @cfg {Number} clicksToEdit
39700      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39701      */
39702     clicksToEdit: 2,
39703
39704     // private
39705     isEditor : true,
39706     // private
39707     trackMouseOver: false, // causes very odd FF errors
39708
39709     onCellDblClick : function(g, row, col){
39710         this.startEditing(row, col);
39711     },
39712
39713     onEditComplete : function(ed, value, startValue){
39714         this.editing = false;
39715         this.activeEditor = null;
39716         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39717         var r = ed.record;
39718         var field = this.colModel.getDataIndex(ed.col);
39719         var e = {
39720             grid: this,
39721             record: r,
39722             field: field,
39723             originalValue: startValue,
39724             value: value,
39725             row: ed.row,
39726             column: ed.col,
39727             cancel:false,
39728             editor: ed
39729         };
39730         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39731         cell.show();
39732           
39733         if(String(value) !== String(startValue)){
39734             
39735             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39736                 r.set(field, e.value);
39737                 // if we are dealing with a combo box..
39738                 // then we also set the 'name' colum to be the displayField
39739                 if (ed.field.displayField && ed.field.name) {
39740                     r.set(ed.field.name, ed.field.el.dom.value);
39741                 }
39742                 
39743                 delete e.cancel; //?? why!!!
39744                 this.fireEvent("afteredit", e);
39745             }
39746         } else {
39747             this.fireEvent("afteredit", e); // always fire it!
39748         }
39749         this.view.focusCell(ed.row, ed.col);
39750     },
39751
39752     /**
39753      * Starts editing the specified for the specified row/column
39754      * @param {Number} rowIndex
39755      * @param {Number} colIndex
39756      */
39757     startEditing : function(row, col){
39758         this.stopEditing();
39759         if(this.colModel.isCellEditable(col, row)){
39760             this.view.ensureVisible(row, col, true);
39761           
39762             var r = this.dataSource.getAt(row);
39763             var field = this.colModel.getDataIndex(col);
39764             var cell = Roo.get(this.view.getCell(row,col));
39765             var e = {
39766                 grid: this,
39767                 record: r,
39768                 field: field,
39769                 value: r.data[field],
39770                 row: row,
39771                 column: col,
39772                 cancel:false 
39773             };
39774             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39775                 this.editing = true;
39776                 var ed = this.colModel.getCellEditor(col, row);
39777                 
39778                 if (!ed) {
39779                     return;
39780                 }
39781                 if(!ed.rendered){
39782                     ed.render(ed.parentEl || document.body);
39783                 }
39784                 ed.field.reset();
39785                
39786                 cell.hide();
39787                 
39788                 (function(){ // complex but required for focus issues in safari, ie and opera
39789                     ed.row = row;
39790                     ed.col = col;
39791                     ed.record = r;
39792                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39793                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39794                     this.activeEditor = ed;
39795                     var v = r.data[field];
39796                     ed.startEdit(this.view.getCell(row, col), v);
39797                     // combo's with 'displayField and name set
39798                     if (ed.field.displayField && ed.field.name) {
39799                         ed.field.el.dom.value = r.data[ed.field.name];
39800                     }
39801                     
39802                     
39803                 }).defer(50, this);
39804             }
39805         }
39806     },
39807         
39808     /**
39809      * Stops any active editing
39810      */
39811     stopEditing : function(){
39812         if(this.activeEditor){
39813             this.activeEditor.completeEdit();
39814         }
39815         this.activeEditor = null;
39816     },
39817         
39818          /**
39819      * Called to get grid's drag proxy text, by default returns this.ddText.
39820      * @return {String}
39821      */
39822     getDragDropText : function(){
39823         var count = this.selModel.getSelectedCell() ? 1 : 0;
39824         return String.format(this.ddText, count, count == 1 ? '' : 's');
39825     }
39826         
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837
39838 // private - not really -- you end up using it !
39839 // This is a support class used internally by the Grid components
39840
39841 /**
39842  * @class Roo.grid.GridEditor
39843  * @extends Roo.Editor
39844  * Class for creating and editable grid elements.
39845  * @param {Object} config any settings (must include field)
39846  */
39847 Roo.grid.GridEditor = function(field, config){
39848     if (!config && field.field) {
39849         config = field;
39850         field = Roo.factory(config.field, Roo.form);
39851     }
39852     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39853     field.monitorTab = false;
39854 };
39855
39856 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39857     
39858     /**
39859      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39860      */
39861     
39862     alignment: "tl-tl",
39863     autoSize: "width",
39864     hideEl : false,
39865     cls: "x-small-editor x-grid-editor",
39866     shim:false,
39867     shadow:"frame"
39868 });/*
39869  * Based on:
39870  * Ext JS Library 1.1.1
39871  * Copyright(c) 2006-2007, Ext JS, LLC.
39872  *
39873  * Originally Released Under LGPL - original licence link has changed is not relivant.
39874  *
39875  * Fork - LGPL
39876  * <script type="text/javascript">
39877  */
39878   
39879
39880   
39881 Roo.grid.PropertyRecord = Roo.data.Record.create([
39882     {name:'name',type:'string'},  'value'
39883 ]);
39884
39885
39886 Roo.grid.PropertyStore = function(grid, source){
39887     this.grid = grid;
39888     this.store = new Roo.data.Store({
39889         recordType : Roo.grid.PropertyRecord
39890     });
39891     this.store.on('update', this.onUpdate,  this);
39892     if(source){
39893         this.setSource(source);
39894     }
39895     Roo.grid.PropertyStore.superclass.constructor.call(this);
39896 };
39897
39898
39899
39900 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39901     setSource : function(o){
39902         this.source = o;
39903         this.store.removeAll();
39904         var data = [];
39905         for(var k in o){
39906             if(this.isEditableValue(o[k])){
39907                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39908             }
39909         }
39910         this.store.loadRecords({records: data}, {}, true);
39911     },
39912
39913     onUpdate : function(ds, record, type){
39914         if(type == Roo.data.Record.EDIT){
39915             var v = record.data['value'];
39916             var oldValue = record.modified['value'];
39917             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39918                 this.source[record.id] = v;
39919                 record.commit();
39920                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39921             }else{
39922                 record.reject();
39923             }
39924         }
39925     },
39926
39927     getProperty : function(row){
39928        return this.store.getAt(row);
39929     },
39930
39931     isEditableValue: function(val){
39932         if(val && val instanceof Date){
39933             return true;
39934         }else if(typeof val == 'object' || typeof val == 'function'){
39935             return false;
39936         }
39937         return true;
39938     },
39939
39940     setValue : function(prop, value){
39941         this.source[prop] = value;
39942         this.store.getById(prop).set('value', value);
39943     },
39944
39945     getSource : function(){
39946         return this.source;
39947     }
39948 });
39949
39950 Roo.grid.PropertyColumnModel = function(grid, store){
39951     this.grid = grid;
39952     var g = Roo.grid;
39953     g.PropertyColumnModel.superclass.constructor.call(this, [
39954         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39955         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39956     ]);
39957     this.store = store;
39958     this.bselect = Roo.DomHelper.append(document.body, {
39959         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39960             {tag: 'option', value: 'true', html: 'true'},
39961             {tag: 'option', value: 'false', html: 'false'}
39962         ]
39963     });
39964     Roo.id(this.bselect);
39965     var f = Roo.form;
39966     this.editors = {
39967         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39968         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39969         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39970         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39971         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39972     };
39973     this.renderCellDelegate = this.renderCell.createDelegate(this);
39974     this.renderPropDelegate = this.renderProp.createDelegate(this);
39975 };
39976
39977 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39978     
39979     
39980     nameText : 'Name',
39981     valueText : 'Value',
39982     
39983     dateFormat : 'm/j/Y',
39984     
39985     
39986     renderDate : function(dateVal){
39987         return dateVal.dateFormat(this.dateFormat);
39988     },
39989
39990     renderBool : function(bVal){
39991         return bVal ? 'true' : 'false';
39992     },
39993
39994     isCellEditable : function(colIndex, rowIndex){
39995         return colIndex == 1;
39996     },
39997
39998     getRenderer : function(col){
39999         return col == 1 ?
40000             this.renderCellDelegate : this.renderPropDelegate;
40001     },
40002
40003     renderProp : function(v){
40004         return this.getPropertyName(v);
40005     },
40006
40007     renderCell : function(val){
40008         var rv = val;
40009         if(val instanceof Date){
40010             rv = this.renderDate(val);
40011         }else if(typeof val == 'boolean'){
40012             rv = this.renderBool(val);
40013         }
40014         return Roo.util.Format.htmlEncode(rv);
40015     },
40016
40017     getPropertyName : function(name){
40018         var pn = this.grid.propertyNames;
40019         return pn && pn[name] ? pn[name] : name;
40020     },
40021
40022     getCellEditor : function(colIndex, rowIndex){
40023         var p = this.store.getProperty(rowIndex);
40024         var n = p.data['name'], val = p.data['value'];
40025         
40026         if(typeof(this.grid.customEditors[n]) == 'string'){
40027             return this.editors[this.grid.customEditors[n]];
40028         }
40029         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40030             return this.grid.customEditors[n];
40031         }
40032         if(val instanceof Date){
40033             return this.editors['date'];
40034         }else if(typeof val == 'number'){
40035             return this.editors['number'];
40036         }else if(typeof val == 'boolean'){
40037             return this.editors['boolean'];
40038         }else{
40039             return this.editors['string'];
40040         }
40041     }
40042 });
40043
40044 /**
40045  * @class Roo.grid.PropertyGrid
40046  * @extends Roo.grid.EditorGrid
40047  * This class represents the  interface of a component based property grid control.
40048  * <br><br>Usage:<pre><code>
40049  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40050       
40051  });
40052  // set any options
40053  grid.render();
40054  * </code></pre>
40055   
40056  * @constructor
40057  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40058  * The container MUST have some type of size defined for the grid to fill. The container will be
40059  * automatically set to position relative if it isn't already.
40060  * @param {Object} config A config object that sets properties on this grid.
40061  */
40062 Roo.grid.PropertyGrid = function(container, config){
40063     config = config || {};
40064     var store = new Roo.grid.PropertyStore(this);
40065     this.store = store;
40066     var cm = new Roo.grid.PropertyColumnModel(this, store);
40067     store.store.sort('name', 'ASC');
40068     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40069         ds: store.store,
40070         cm: cm,
40071         enableColLock:false,
40072         enableColumnMove:false,
40073         stripeRows:false,
40074         trackMouseOver: false,
40075         clicksToEdit:1
40076     }, config));
40077     this.getGridEl().addClass('x-props-grid');
40078     this.lastEditRow = null;
40079     this.on('columnresize', this.onColumnResize, this);
40080     this.addEvents({
40081          /**
40082              * @event beforepropertychange
40083              * Fires before a property changes (return false to stop?)
40084              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40085              * @param {String} id Record Id
40086              * @param {String} newval New Value
40087          * @param {String} oldval Old Value
40088              */
40089         "beforepropertychange": true,
40090         /**
40091              * @event propertychange
40092              * Fires after a property changes
40093              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40094              * @param {String} id Record Id
40095              * @param {String} newval New Value
40096          * @param {String} oldval Old Value
40097              */
40098         "propertychange": true
40099     });
40100     this.customEditors = this.customEditors || {};
40101 };
40102 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40103     
40104      /**
40105      * @cfg {Object} customEditors map of colnames=> custom editors.
40106      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40107      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40108      * false disables editing of the field.
40109          */
40110     
40111       /**
40112      * @cfg {Object} propertyNames map of property Names to their displayed value
40113          */
40114     
40115     render : function(){
40116         Roo.grid.PropertyGrid.superclass.render.call(this);
40117         this.autoSize.defer(100, this);
40118     },
40119
40120     autoSize : function(){
40121         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40122         if(this.view){
40123             this.view.fitColumns();
40124         }
40125     },
40126
40127     onColumnResize : function(){
40128         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40129         this.autoSize();
40130     },
40131     /**
40132      * Sets the data for the Grid
40133      * accepts a Key => Value object of all the elements avaiable.
40134      * @param {Object} data  to appear in grid.
40135      */
40136     setSource : function(source){
40137         this.store.setSource(source);
40138         //this.autoSize();
40139     },
40140     /**
40141      * Gets all the data from the grid.
40142      * @return {Object} data  data stored in grid
40143      */
40144     getSource : function(){
40145         return this.store.getSource();
40146     }
40147 });/*
40148  * Based on:
40149  * Ext JS Library 1.1.1
40150  * Copyright(c) 2006-2007, Ext JS, LLC.
40151  *
40152  * Originally Released Under LGPL - original licence link has changed is not relivant.
40153  *
40154  * Fork - LGPL
40155  * <script type="text/javascript">
40156  */
40157  
40158 /**
40159  * @class Roo.LoadMask
40160  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40161  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40162  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40163  * element's UpdateManager load indicator and will be destroyed after the initial load.
40164  * @constructor
40165  * Create a new LoadMask
40166  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40167  * @param {Object} config The config object
40168  */
40169 Roo.LoadMask = function(el, config){
40170     this.el = Roo.get(el);
40171     Roo.apply(this, config);
40172     if(this.store){
40173         this.store.on('beforeload', this.onBeforeLoad, this);
40174         this.store.on('load', this.onLoad, this);
40175         this.store.on('loadexception', this.onLoadException, this);
40176         this.removeMask = false;
40177     }else{
40178         var um = this.el.getUpdateManager();
40179         um.showLoadIndicator = false; // disable the default indicator
40180         um.on('beforeupdate', this.onBeforeLoad, this);
40181         um.on('update', this.onLoad, this);
40182         um.on('failure', this.onLoad, this);
40183         this.removeMask = true;
40184     }
40185 };
40186
40187 Roo.LoadMask.prototype = {
40188     /**
40189      * @cfg {Boolean} removeMask
40190      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40191      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40192      */
40193     /**
40194      * @cfg {String} msg
40195      * The text to display in a centered loading message box (defaults to 'Loading...')
40196      */
40197     msg : 'Loading...',
40198     /**
40199      * @cfg {String} msgCls
40200      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40201      */
40202     msgCls : 'x-mask-loading',
40203
40204     /**
40205      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40206      * @type Boolean
40207      */
40208     disabled: false,
40209
40210     /**
40211      * Disables the mask to prevent it from being displayed
40212      */
40213     disable : function(){
40214        this.disabled = true;
40215     },
40216
40217     /**
40218      * Enables the mask so that it can be displayed
40219      */
40220     enable : function(){
40221         this.disabled = false;
40222     },
40223     
40224     onLoadException : function()
40225     {
40226         Roo.log(arguments);
40227         
40228         if (typeof(arguments[3]) != 'undefined') {
40229             Roo.MessageBox.alert("Error loading",arguments[3]);
40230         } 
40231         /*
40232         try {
40233             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40234                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40235             }   
40236         } catch(e) {
40237             
40238         }
40239         */
40240     
40241         
40242         
40243         this.el.unmask(this.removeMask);
40244     },
40245     // private
40246     onLoad : function()
40247     {
40248         this.el.unmask(this.removeMask);
40249     },
40250
40251     // private
40252     onBeforeLoad : function(){
40253         if(!this.disabled){
40254             this.el.mask(this.msg, this.msgCls);
40255         }
40256     },
40257
40258     // private
40259     destroy : function(){
40260         if(this.store){
40261             this.store.un('beforeload', this.onBeforeLoad, this);
40262             this.store.un('load', this.onLoad, this);
40263             this.store.un('loadexception', this.onLoadException, this);
40264         }else{
40265             var um = this.el.getUpdateManager();
40266             um.un('beforeupdate', this.onBeforeLoad, this);
40267             um.un('update', this.onLoad, this);
40268             um.un('failure', this.onLoad, this);
40269         }
40270     }
40271 };/*
40272  * Based on:
40273  * Ext JS Library 1.1.1
40274  * Copyright(c) 2006-2007, Ext JS, LLC.
40275  *
40276  * Originally Released Under LGPL - original licence link has changed is not relivant.
40277  *
40278  * Fork - LGPL
40279  * <script type="text/javascript">
40280  */
40281
40282
40283 /**
40284  * @class Roo.XTemplate
40285  * @extends Roo.Template
40286  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40287 <pre><code>
40288 var t = new Roo.XTemplate(
40289         '&lt;select name="{name}"&gt;',
40290                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40291         '&lt;/select&gt;'
40292 );
40293  
40294 // then append, applying the master template values
40295  </code></pre>
40296  *
40297  * Supported features:
40298  *
40299  *  Tags:
40300
40301 <pre><code>
40302       {a_variable} - output encoded.
40303       {a_variable.format:("Y-m-d")} - call a method on the variable
40304       {a_variable:raw} - unencoded output
40305       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40306       {a_variable:this.method_on_template(...)} - call a method on the template object.
40307  
40308 </code></pre>
40309  *  The tpl tag:
40310 <pre><code>
40311         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40312         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40313         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40314         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40315   
40316         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40317         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40318 </code></pre>
40319  *      
40320  */
40321 Roo.XTemplate = function()
40322 {
40323     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40324     if (this.html) {
40325         this.compile();
40326     }
40327 };
40328
40329
40330 Roo.extend(Roo.XTemplate, Roo.Template, {
40331
40332     /**
40333      * The various sub templates
40334      */
40335     tpls : false,
40336     /**
40337      *
40338      * basic tag replacing syntax
40339      * WORD:WORD()
40340      *
40341      * // you can fake an object call by doing this
40342      *  x.t:(test,tesT) 
40343      * 
40344      */
40345     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40346
40347     /**
40348      * compile the template
40349      *
40350      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40351      *
40352      */
40353     compile: function()
40354     {
40355         var s = this.html;
40356      
40357         s = ['<tpl>', s, '</tpl>'].join('');
40358     
40359         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40360             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40361             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40362             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40363             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40364             m,
40365             id     = 0,
40366             tpls   = [];
40367     
40368         while(true == !!(m = s.match(re))){
40369             var forMatch   = m[0].match(nameRe),
40370                 ifMatch   = m[0].match(ifRe),
40371                 execMatch   = m[0].match(execRe),
40372                 namedMatch   = m[0].match(namedRe),
40373                 
40374                 exp  = null, 
40375                 fn   = null,
40376                 exec = null,
40377                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40378                 
40379             if (ifMatch) {
40380                 // if - puts fn into test..
40381                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40382                 if(exp){
40383                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40384                 }
40385             }
40386             
40387             if (execMatch) {
40388                 // exec - calls a function... returns empty if true is  returned.
40389                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40390                 if(exp){
40391                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40392                 }
40393             }
40394             
40395             
40396             if (name) {
40397                 // for = 
40398                 switch(name){
40399                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40400                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40401                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40402                 }
40403             }
40404             var uid = namedMatch ? namedMatch[1] : id;
40405             
40406             
40407             tpls.push({
40408                 id:     namedMatch ? namedMatch[1] : id,
40409                 target: name,
40410                 exec:   exec,
40411                 test:   fn,
40412                 body:   m[1] || ''
40413             });
40414             if (namedMatch) {
40415                 s = s.replace(m[0], '');
40416             } else { 
40417                 s = s.replace(m[0], '{xtpl'+ id + '}');
40418             }
40419             ++id;
40420         }
40421         this.tpls = [];
40422         for(var i = tpls.length-1; i >= 0; --i){
40423             this.compileTpl(tpls[i]);
40424             this.tpls[tpls[i].id] = tpls[i];
40425         }
40426         this.master = tpls[tpls.length-1];
40427         return this;
40428     },
40429     /**
40430      * same as applyTemplate, except it's done to one of the subTemplates
40431      * when using named templates, you can do:
40432      *
40433      * var str = pl.applySubTemplate('your-name', values);
40434      *
40435      * 
40436      * @param {Number} id of the template
40437      * @param {Object} values to apply to template
40438      * @param {Object} parent (normaly the instance of this object)
40439      */
40440     applySubTemplate : function(id, values, parent)
40441     {
40442         
40443         
40444         var t = this.tpls[id];
40445         
40446         
40447         try { 
40448             if(t.test && !t.test.call(this, values, parent)){
40449                 return '';
40450             }
40451         } catch(e) {
40452             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40453             Roo.log(e.toString());
40454             Roo.log(t.test);
40455             return ''
40456         }
40457         try { 
40458             
40459             if(t.exec && t.exec.call(this, values, parent)){
40460                 return '';
40461             }
40462         } catch(e) {
40463             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40464             Roo.log(e.toString());
40465             Roo.log(t.exec);
40466             return ''
40467         }
40468         try {
40469             var vs = t.target ? t.target.call(this, values, parent) : values;
40470             parent = t.target ? values : parent;
40471             if(t.target && vs instanceof Array){
40472                 var buf = [];
40473                 for(var i = 0, len = vs.length; i < len; i++){
40474                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40475                 }
40476                 return buf.join('');
40477             }
40478             return t.compiled.call(this, vs, parent);
40479         } catch (e) {
40480             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40481             Roo.log(e.toString());
40482             Roo.log(t.compiled);
40483             return '';
40484         }
40485     },
40486
40487     compileTpl : function(tpl)
40488     {
40489         var fm = Roo.util.Format;
40490         var useF = this.disableFormats !== true;
40491         var sep = Roo.isGecko ? "+" : ",";
40492         var undef = function(str) {
40493             Roo.log("Property not found :"  + str);
40494             return '';
40495         };
40496         
40497         var fn = function(m, name, format, args)
40498         {
40499             //Roo.log(arguments);
40500             args = args ? args.replace(/\\'/g,"'") : args;
40501             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40502             if (typeof(format) == 'undefined') {
40503                 format= 'htmlEncode';
40504             }
40505             if (format == 'raw' ) {
40506                 format = false;
40507             }
40508             
40509             if(name.substr(0, 4) == 'xtpl'){
40510                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40511             }
40512             
40513             // build an array of options to determine if value is undefined..
40514             
40515             // basically get 'xxxx.yyyy' then do
40516             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40517             //    (function () { Roo.log("Property not found"); return ''; })() :
40518             //    ......
40519             
40520             var udef_ar = [];
40521             var lookfor = '';
40522             Roo.each(name.split('.'), function(st) {
40523                 lookfor += (lookfor.length ? '.': '') + st;
40524                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40525             });
40526             
40527             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40528             
40529             
40530             if(format && useF){
40531                 
40532                 args = args ? ',' + args : "";
40533                  
40534                 if(format.substr(0, 5) != "this."){
40535                     format = "fm." + format + '(';
40536                 }else{
40537                     format = 'this.call("'+ format.substr(5) + '", ';
40538                     args = ", values";
40539                 }
40540                 
40541                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40542             }
40543              
40544             if (args.length) {
40545                 // called with xxyx.yuu:(test,test)
40546                 // change to ()
40547                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40548             }
40549             // raw.. - :raw modifier..
40550             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40551             
40552         };
40553         var body;
40554         // branched to use + in gecko and [].join() in others
40555         if(Roo.isGecko){
40556             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40557                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40558                     "';};};";
40559         }else{
40560             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40561             body.push(tpl.body.replace(/(\r\n|\n)/g,
40562                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40563             body.push("'].join('');};};");
40564             body = body.join('');
40565         }
40566         
40567         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40568        
40569         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40570         eval(body);
40571         
40572         return this;
40573     },
40574
40575     applyTemplate : function(values){
40576         return this.master.compiled.call(this, values, {});
40577         //var s = this.subs;
40578     },
40579
40580     apply : function(){
40581         return this.applyTemplate.apply(this, arguments);
40582     }
40583
40584  });
40585
40586 Roo.XTemplate.from = function(el){
40587     el = Roo.getDom(el);
40588     return new Roo.XTemplate(el.value || el.innerHTML);
40589 };/*
40590  * Original code for Roojs - LGPL
40591  * <script type="text/javascript">
40592  */
40593  
40594 /**
40595  * @class Roo.XComponent
40596  * A delayed Element creator...
40597  * Or a way to group chunks of interface together.
40598  * 
40599  * Mypart.xyx = new Roo.XComponent({
40600
40601     parent : 'Mypart.xyz', // empty == document.element.!!
40602     order : '001',
40603     name : 'xxxx'
40604     region : 'xxxx'
40605     disabled : function() {} 
40606      
40607     tree : function() { // return an tree of xtype declared components
40608         var MODULE = this;
40609         return 
40610         {
40611             xtype : 'NestedLayoutPanel',
40612             // technicall
40613         }
40614      ]
40615  *})
40616  *
40617  *
40618  * It can be used to build a big heiracy, with parent etc.
40619  * or you can just use this to render a single compoent to a dom element
40620  * MYPART.render(Roo.Element | String(id) | dom_element )
40621  * 
40622  * @extends Roo.util.Observable
40623  * @constructor
40624  * @param cfg {Object} configuration of component
40625  * 
40626  */
40627 Roo.XComponent = function(cfg) {
40628     Roo.apply(this, cfg);
40629     this.addEvents({ 
40630         /**
40631              * @event built
40632              * Fires when this the componnt is built
40633              * @param {Roo.XComponent} c the component
40634              */
40635         'built' : true
40636         
40637     });
40638     this.region = this.region || 'center'; // default..
40639     Roo.XComponent.register(this);
40640     this.modules = false;
40641     this.el = false; // where the layout goes..
40642     
40643     
40644 }
40645 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40646     /**
40647      * @property el
40648      * The created element (with Roo.factory())
40649      * @type {Roo.Layout}
40650      */
40651     el  : false,
40652     
40653     /**
40654      * @property el
40655      * for BC  - use el in new code
40656      * @type {Roo.Layout}
40657      */
40658     panel : false,
40659     
40660     /**
40661      * @property layout
40662      * for BC  - use el in new code
40663      * @type {Roo.Layout}
40664      */
40665     layout : false,
40666     
40667      /**
40668      * @cfg {Function|boolean} disabled
40669      * If this module is disabled by some rule, return true from the funtion
40670      */
40671     disabled : false,
40672     
40673     /**
40674      * @cfg {String} parent 
40675      * Name of parent element which it get xtype added to..
40676      */
40677     parent: false,
40678     
40679     /**
40680      * @cfg {String} order
40681      * Used to set the order in which elements are created (usefull for multiple tabs)
40682      */
40683     
40684     order : false,
40685     /**
40686      * @cfg {String} name
40687      * String to display while loading.
40688      */
40689     name : false,
40690     /**
40691      * @cfg {String} region
40692      * Region to render component to (defaults to center)
40693      */
40694     region : 'center',
40695     
40696     /**
40697      * @cfg {Array} items
40698      * A single item array - the first element is the root of the tree..
40699      * It's done this way to stay compatible with the Xtype system...
40700      */
40701     items : false,
40702     
40703     /**
40704      * @property _tree
40705      * The method that retuns the tree of parts that make up this compoennt 
40706      * @type {function}
40707      */
40708     _tree  : false,
40709     
40710      /**
40711      * render
40712      * render element to dom or tree
40713      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40714      */
40715     
40716     render : function(el)
40717     {
40718         
40719         el = el || false;
40720         var hp = this.parent ? 1 : 0;
40721         
40722         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40723             // if parent is a '#.....' string, then let's use that..
40724             var ename = this.parent.substr(1)
40725             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
40726             el = Roo.get(ename);
40727             if (!el && !this.parent) {
40728                 Roo.log("Warning - element can not be found :#" + ename );
40729                 return;
40730             }
40731         }
40732         
40733         
40734         if (!this.parent) {
40735             
40736             el = el ? Roo.get(el) : false;      
40737             
40738             // it's a top level one..
40739             this.parent =  {
40740                 el : new Roo.BorderLayout(el || document.body, {
40741                 
40742                      center: {
40743                          titlebar: false,
40744                          autoScroll:false,
40745                          closeOnTab: true,
40746                          tabPosition: 'top',
40747                           //resizeTabs: true,
40748                          alwaysShowTabs: el && hp? false :  true,
40749                          hideTabs: el || !hp ? true :  false,
40750                          minTabWidth: 140
40751                      }
40752                  })
40753             }
40754         }
40755         
40756                 if (!this.parent.el) {
40757                         // probably an old style ctor, which has been disabled.
40758                         return;
40759                         
40760                 }
40761                 // The 'tree' method is  '_tree now' 
40762             
40763         var tree = this._tree ? this._tree() : this.tree();
40764         tree.region = tree.region || this.region;
40765         this.el = this.parent.el.addxtype(tree);
40766         this.fireEvent('built', this);
40767         
40768         this.panel = this.el;
40769         this.layout = this.panel.layout;
40770                 this.parentLayout = this.parent.layout  || false;  
40771          
40772     }
40773     
40774 });
40775
40776 Roo.apply(Roo.XComponent, {
40777     /**
40778      * @property  hideProgress
40779      * true to disable the building progress bar.. usefull on single page renders.
40780      * @type Boolean
40781      */
40782     hideProgress : false,
40783     /**
40784      * @property  buildCompleted
40785      * True when the builder has completed building the interface.
40786      * @type Boolean
40787      */
40788     buildCompleted : false,
40789      
40790     /**
40791      * @property  topModule
40792      * the upper most module - uses document.element as it's constructor.
40793      * @type Object
40794      */
40795      
40796     topModule  : false,
40797       
40798     /**
40799      * @property  modules
40800      * array of modules to be created by registration system.
40801      * @type {Array} of Roo.XComponent
40802      */
40803     
40804     modules : [],
40805     /**
40806      * @property  elmodules
40807      * array of modules to be created by which use #ID 
40808      * @type {Array} of Roo.XComponent
40809      */
40810      
40811     elmodules : [],
40812
40813     
40814     /**
40815      * Register components to be built later.
40816      *
40817      * This solves the following issues
40818      * - Building is not done on page load, but after an authentication process has occured.
40819      * - Interface elements are registered on page load
40820      * - Parent Interface elements may not be loaded before child, so this handles that..
40821      * 
40822      *
40823      * example:
40824      * 
40825      * MyApp.register({
40826           order : '000001',
40827           module : 'Pman.Tab.projectMgr',
40828           region : 'center',
40829           parent : 'Pman.layout',
40830           disabled : false,  // or use a function..
40831         })
40832      
40833      * * @param {Object} details about module
40834      */
40835     register : function(obj) {
40836                 
40837         Roo.XComponent.event.fireEvent('register', obj);
40838         switch(typeof(obj.disabled) ) {
40839                 
40840             case 'undefined':
40841                 break;
40842             
40843             case 'function':
40844                 if ( obj.disabled() ) {
40845                         return;
40846                 }
40847                 break;
40848             
40849             default:
40850                 if (obj.disabled) {
40851                         return;
40852                 }
40853                 break;
40854         }
40855                 
40856         this.modules.push(obj);
40857          
40858     },
40859     /**
40860      * convert a string to an object..
40861      * eg. 'AAA.BBB' -> finds AAA.BBB
40862
40863      */
40864     
40865     toObject : function(str)
40866     {
40867         if (!str || typeof(str) == 'object') {
40868             return str;
40869         }
40870         if (str.substring(0,1) == '#') {
40871             return str;
40872         }
40873
40874         var ar = str.split('.');
40875         var rt, o;
40876         rt = ar.shift();
40877             /** eval:var:o */
40878         try {
40879             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40880         } catch (e) {
40881             throw "Module not found : " + str;
40882         }
40883         
40884         if (o === false) {
40885             throw "Module not found : " + str;
40886         }
40887         Roo.each(ar, function(e) {
40888             if (typeof(o[e]) == 'undefined') {
40889                 throw "Module not found : " + str;
40890             }
40891             o = o[e];
40892         });
40893         
40894         return o;
40895         
40896     },
40897     
40898     
40899     /**
40900      * move modules into their correct place in the tree..
40901      * 
40902      */
40903     preBuild : function ()
40904     {
40905         var _t = this;
40906         Roo.each(this.modules , function (obj)
40907         {
40908             Roo.XComponent.event.fireEvent('beforebuild', obj);
40909             
40910             var opar = obj.parent;
40911             try { 
40912                 obj.parent = this.toObject(opar);
40913             } catch(e) {
40914                 Roo.log("parent:toObject failed: " + e.toString());
40915                 return;
40916             }
40917             
40918             if (!obj.parent) {
40919                 Roo.debug && Roo.log("GOT top level module");
40920                 Roo.debug && Roo.log(obj);
40921                 obj.modules = new Roo.util.MixedCollection(false, 
40922                     function(o) { return o.order + '' }
40923                 );
40924                 this.topModule = obj;
40925                 return;
40926             }
40927                         // parent is a string (usually a dom element name..)
40928             if (typeof(obj.parent) == 'string') {
40929                 this.elmodules.push(obj);
40930                 return;
40931             }
40932             if (obj.parent.constructor != Roo.XComponent) {
40933                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40934             }
40935             if (!obj.parent.modules) {
40936                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40937                     function(o) { return o.order + '' }
40938                 );
40939             }
40940             if (obj.parent.disabled) {
40941                 obj.disabled = true;
40942             }
40943             obj.parent.modules.add(obj);
40944         }, this);
40945     },
40946     
40947      /**
40948      * make a list of modules to build.
40949      * @return {Array} list of modules. 
40950      */ 
40951     
40952     buildOrder : function()
40953     {
40954         var _this = this;
40955         var cmp = function(a,b) {   
40956             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40957         };
40958         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40959             throw "No top level modules to build";
40960         }
40961         
40962         // make a flat list in order of modules to build.
40963         var mods = this.topModule ? [ this.topModule ] : [];
40964                 
40965         
40966         // elmodules (is a list of DOM based modules )
40967         Roo.each(this.elmodules, function(e) {
40968             mods.push(e);
40969             if (!this.topModule &&
40970                 typeof(e.parent) == 'string' &&
40971                 e.parent.substring(0,1) == '#' &&
40972                 Roo.get(e.parent.substr(1))
40973                ) {
40974                 
40975                 _this.topModule = e;
40976             }
40977             
40978         });
40979
40980         
40981         // add modules to their parents..
40982         var addMod = function(m) {
40983             Roo.debug && Roo.log("build Order: add: " + m.name);
40984                 
40985             mods.push(m);
40986             if (m.modules && !m.disabled) {
40987                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40988                 m.modules.keySort('ASC',  cmp );
40989                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40990     
40991                 m.modules.each(addMod);
40992             } else {
40993                 Roo.debug && Roo.log("build Order: no child modules");
40994             }
40995             // not sure if this is used any more..
40996             if (m.finalize) {
40997                 m.finalize.name = m.name + " (clean up) ";
40998                 mods.push(m.finalize);
40999             }
41000             
41001         }
41002         if (this.topModule && this.topModule.modules) { 
41003             this.topModule.modules.keySort('ASC',  cmp );
41004             this.topModule.modules.each(addMod);
41005         } 
41006         return mods;
41007     },
41008     
41009      /**
41010      * Build the registered modules.
41011      * @param {Object} parent element.
41012      * @param {Function} optional method to call after module has been added.
41013      * 
41014      */ 
41015    
41016     build : function() 
41017     {
41018         
41019         this.preBuild();
41020         var mods = this.buildOrder();
41021       
41022         //this.allmods = mods;
41023         //Roo.debug && Roo.log(mods);
41024         //return;
41025         if (!mods.length) { // should not happen
41026             throw "NO modules!!!";
41027         }
41028         
41029         
41030         var msg = "Building Interface...";
41031         // flash it up as modal - so we store the mask!?
41032         if (!this.hideProgress) {
41033             Roo.MessageBox.show({ title: 'loading' });
41034             Roo.MessageBox.show({
41035                title: "Please wait...",
41036                msg: msg,
41037                width:450,
41038                progress:true,
41039                closable:false,
41040                modal: false
41041               
41042             });
41043         }
41044         var total = mods.length;
41045         
41046         var _this = this;
41047         var progressRun = function() {
41048             if (!mods.length) {
41049                 Roo.debug && Roo.log('hide?');
41050                 if (!this.hideProgress) {
41051                     Roo.MessageBox.hide();
41052                 }
41053                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41054                 
41055                 // THE END...
41056                 return false;   
41057             }
41058             
41059             var m = mods.shift();
41060             
41061             
41062             Roo.debug && Roo.log(m);
41063             // not sure if this is supported any more.. - modules that are are just function
41064             if (typeof(m) == 'function') { 
41065                 m.call(this);
41066                 return progressRun.defer(10, _this);
41067             } 
41068             
41069             
41070             msg = "Building Interface " + (total  - mods.length) + 
41071                     " of " + total + 
41072                     (m.name ? (' - ' + m.name) : '');
41073                         Roo.debug && Roo.log(msg);
41074             if (!this.hideProgress) { 
41075                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41076             }
41077             
41078          
41079             // is the module disabled?
41080             var disabled = (typeof(m.disabled) == 'function') ?
41081                 m.disabled.call(m.module.disabled) : m.disabled;    
41082             
41083             
41084             if (disabled) {
41085                 return progressRun(); // we do not update the display!
41086             }
41087             
41088             // now build 
41089             
41090                         
41091                         
41092             m.render();
41093             // it's 10 on top level, and 1 on others??? why...
41094             return progressRun.defer(10, _this);
41095              
41096         }
41097         progressRun.defer(1, _this);
41098      
41099         
41100         
41101     },
41102         
41103         
41104         /**
41105          * Event Object.
41106          *
41107          *
41108          */
41109         event: false, 
41110     /**
41111          * wrapper for event.on - aliased later..  
41112          * Typically use to register a event handler for register:
41113          *
41114          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41115          *
41116          */
41117     on : false
41118    
41119     
41120     
41121 });
41122
41123 Roo.XComponent.event = new Roo.util.Observable({
41124                 events : { 
41125                         /**
41126                          * @event register
41127                          * Fires when an Component is registered,
41128                          * set the disable property on the Component to stop registration.
41129                          * @param {Roo.XComponent} c the component being registerd.
41130                          * 
41131                          */
41132                         'register' : true,
41133             /**
41134                          * @event beforebuild
41135                          * Fires before each Component is built
41136                          * can be used to apply permissions.
41137                          * @param {Roo.XComponent} c the component being registerd.
41138                          * 
41139                          */
41140                         'beforebuild' : true,
41141                         /**
41142                          * @event buildcomplete
41143                          * Fires on the top level element when all elements have been built
41144                          * @param {Roo.XComponent} the top level component.
41145                          */
41146                         'buildcomplete' : true
41147                         
41148                 }
41149 });
41150
41151 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41152